Documentation v1.0.2

Preview

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">
        <i class="ki-duotone ki-magnifier fs-1 position-absolute ms-6"><span class="path1"></span><span class="path2"></span></i>
        <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">
            <i class="ki-duotone ki-filter fs-2"><span class="path1"></span><span class="path2"></span></i>
            Filter
        </button>
        <!--end::Filter-->

        <!--begin::Add customer-->
        <button type="button" class="btn btn-primary" data-bs-toggle="tooltip" title="Coming Soon">
            <i class="ki-duotone ki-plus fs-2"></i>
            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-bold 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-500 fw-bold 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-semibold">
    </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 fs-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="currentColor" 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();
});
[
    {
        "RecordID": 1,
        "OrderID": "49349-387",
        "Name": "Dugald Beastall",
        "Email": "dbeastall0@ezinearticles.com",
        "Address": "88 Scott Crossing",
        "City": "Daze",
        "Country": "China",
        "CountryCode": "CN",
        "Company": "Hammes-Mitchell",
        "Currency": "CNY",
        "Notes": "mus vivamus vestibulum sagittis sapien cum sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus etiam vel augue",
        "Department": "Human Resources",
        "Website": "amazon.com",
        "Latitude": 20.0429063,
        "Longitude": 110.354037,
        "Datetime": "2020-04-10 05:48:26",
        "TimeZone": "Asia\/Chongqing",
        "Money": "$178460.50",
        "Gender": "M",
        "CreditCardNumber": "4682474206308",
        "CreditCardType": "visa"
    },
    {
        "RecordID": 2,
        "OrderID": "51808-213",
        "Name": "Aristotle Aicken",
        "Email": "aaicken1@friendfeed.com",
        "Address": "4 Merrick Court",
        "City": "Hobo",
        "Country": "Philippines",
        "CountryCode": "PH",
        "Company": "Schimmel, Towne and Torp",
        "Currency": "PHP",
        "Notes": "sed sagittis nam congue risus semper porta volutpat quam pede",
        "Department": "Sales",
        "Website": "answers.com",
        "Latitude": 13.5085076,
        "Longitude": 123.1804731,
        "Datetime": "2020-10-02 05:40:48",
        "TimeZone": "Asia\/Manila",
        "Money": "$1150804.73",
        "Gender": "M",
        "CreditCardNumber": "372301415416298",
        "CreditCardType": "americanexpress"
    },
    {
        "RecordID": 3,
        "OrderID": "58443-0015",
        "Name": "Yvonne Comiam",
        "Email": "ycomiam2@oracle.com",
        "Address": "5284 Fieldstone Parkway",
        "City": "Damu",
        "Country": "China",
        "CountryCode": "CN",
        "Company": "Padberg Group",
        "Currency": "CNY",
        "Notes": "luctus et ultrices posuere cubilia curae nulla dapibus dolor vel est donec odio justo sollicitudin ut suscipit a feugiat et",
        "Department": "Engineering",
        "Website": "ox.ac.uk",
        "Latitude": 24.134518,
        "Longitude": 111.485304,
        "Datetime": "2020-07-08 23:05:11",
        "TimeZone": "Asia\/Shanghai",
        "Money": "$54341.33",
        "Gender": "F",
        "CreditCardNumber": "4017956514996",
        "CreditCardType": "visa"
    },
    {
        "RecordID": 4,
        "OrderID": "49288-0189",
        "Name": "Benton Reynalds",
        "Email": "breynalds3@studiopress.com",
        "Address": "65026 Ruskin Lane",
        "City": "Gelap",
        "Country": "Indonesia",
        "CountryCode": "ID",
        "Company": "Koss and Sons",
        "Currency": "IDR",
        "Notes": "volutpat quam pede lobortis ligula sit amet eleifend pede libero quis orci nullam molestie nibh in",
        "Department": "Business Development",
        "Website": "dot.gov",
        "Latitude": -6.9654201,
        "Longitude": 112.2443628,
        "Datetime": "2020-11-24 22:01:00",
        "TimeZone": "Asia\/Jakarta",
        "Money": "$77709.51",
        "Gender": "M",
        "CreditCardNumber": "337941293027131",
        "CreditCardType": "americanexpress"
    },
    {
        "RecordID": 5,
        "OrderID": "10675-002",
        "Name": "Cody Drysdell",
        "Email": "cdrysdell4@youku.com",
        "Address": "80916 Dottie Hill",
        "City": "Ne\u1e96alim",
        "Country": "Israel",
        "CountryCode": "IL",
        "Company": "Boyer Group",
        "Currency": "ILS",
        "Notes": "maecenas ut massa quis augue luctus tincidunt nulla mollis molestie lorem quisque ut erat curabitur gravida nisi at nibh",
        "Department": "Training",
        "Website": "huffingtonpost.com",
        "Latitude": 32.058597,
        "Longitude": 34.9136,
        "Datetime": "2020-12-03 20:02:41",
        "TimeZone": "Asia\/Jerusalem",
        "Money": "$1045848.44",
        "Gender": "F",
        "CreditCardNumber": "372301498885559",
        "CreditCardType": "americanexpress"
    },
    {
        "RecordID": 6,
        "OrderID": "35356-028",
        "Name": "Sebastiano Mingard",
        "Email": "smingard5@wsj.com",
        "Address": "6121 Armistice Road",
        "City": "Starcevica",
        "Country": "Bosnia and Herzegovina",
        "CountryCode": "BA",
        "Company": "Kiehn-Runte",
        "Currency": "BAM",
        "Notes": "a odio in hac habitasse platea dictumst maecenas ut massa quis augue luctus tincidunt nulla mollis molestie lorem quisque ut",
        "Department": "Business Development",
        "Website": "cbsnews.com",
        "Latitude": 44.7607693,
        "Longitude": 17.2038547,
        "Datetime": "2020-07-05 07:12:56",
        "TimeZone": "Europe\/Sarajevo",
        "Money": "$703053.38",
        "Gender": "M",
        "CreditCardNumber": "4017956322929",
        "CreditCardType": "visa"
    },
    {
        "RecordID": 7,
        "OrderID": "54868-5792",
        "Name": "Nickie Debill",
        "Email": "ndebill6@weebly.com",
        "Address": "92202 Rutledge Avenue",
        "City": "Greenwood",
        "Country": "Canada",
        "CountryCode": "CA",
        "Company": "Wiza-Blanda",
        "Currency": "CAD",
        "Notes": "eros vestibulum ac est lacinia nisi venenatis tristique fusce congue diam",
        "Department": "Business Development",
        "Website": "printfriendly.com",
        "Latitude": 44.98345,
        "Longitude": -64.89879,
        "Datetime": "2020-01-29 04:28:02",
        "TimeZone": "America\/Moncton",
        "Money": "$564744.00",
        "Gender": "F",
        "CreditCardNumber": "337941218508546",
        "CreditCardType": "americanexpress"
    },
    {
        "RecordID": 8,
        "OrderID": "11822-0656",
        "Name": "Anstice Ygoe",
        "Email": "aygoe7@japanpost.jp",
        "Address": "6 Hollow Ridge Junction",
        "City": "Wushi",
        "Country": "China",
        "CountryCode": "CN",
        "Company": "Goodwin LLC",
        "Currency": "CNY",
        "Notes": "sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur",
        "Department": "Legal",
        "Website": "netlog.com",
        "Latitude": 31.491169,
        "Longitude": 120.31191,
        "Datetime": "2020-01-21 06:16:04",
        "TimeZone": "Asia\/Chongqing",
        "Money": "$1099551.54",
        "Gender": "F",
        "CreditCardNumber": "5438780052553151",
        "CreditCardType": "mastercard"
    },
    {
        "RecordID": 9,
        "OrderID": "60681-3006",
        "Name": "Iseabal Bottomley",
        "Email": "ibottomley8@reverbnation.com",
        "Address": "225 Birchwood Junction",
        "City": "Timbuktu",
        "Country": "Mali",
        "CountryCode": "ML",
        "Company": "Stark-Mills",
        "Currency": "XOF",
        "Notes": "varius ut blandit non interdum in ante vestibulum ante ipsum primis in faucibus orci luctus et",
        "Department": "Human Resources",
        "Website": "themeforest.net",
        "Latitude": 16.7665887,
        "Longitude": -3.0025615,
        "Datetime": "2020-07-02 19:36:35",
        "TimeZone": "Africa\/Bamako",
        "Money": "$137510.03",
        "Gender": "F",
        "CreditCardNumber": "5358043968138476",
        "CreditCardType": "mastercard"
    },
    {
        "RecordID": 10,
        "OrderID": "75847-1501",
        "Name": "Cobby McWhirter",
        "Email": "cmcwhirter9@goo.ne.jp",
        "Address": "50557 Hudson Court",
        "City": "Villa Gesell",
        "Country": "Argentina",
        "CountryCode": "AR",
        "Company": "Bruen, Kutch and Bartell",
        "Currency": "ARS",
        "Notes": "suscipit nulla elit ac nulla sed vel enim sit amet",
        "Department": "Engineering",
        "Website": "vimeo.com",
        "Latitude": -34.7368519,
        "Longitude": -58.5019467,
        "Datetime": "2020-11-18 00:32:20",
        "TimeZone": "America\/Argentina\/Buenos_Aires",
        "Money": "$189903.13",
        "Gender": "M",
        "CreditCardNumber": "5339559580646442",
        "CreditCardType": "mastercard"
    }
]
<?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);
}
Preview Get Help Buy Now