allanmcarvalho/cakephp-datatables

View on GitHub
src/Table/Option/Section/OptionsOption.php

Summary

Maintainability
D
1 day
Test Coverage
<?php
/**
 * Copyright (c) Allan Carvalho 2020.
 * Under Mit License
 * php version 7.2
 *
 * link     https://github.com/allanmcarvalho/cakephp-data-renderer
 * author   Allan Carvalho <allan.m.carvalho@outlook.com>
 */
declare(strict_types = 1);

namespace DataTables\Table\Option\Section;

use Cake\Error\FatalErrorException;
use Cake\Utility\Text;
use DataTables\Table\Option\ChildOptionAbstract;
use DataTables\Table\Option\MainOption;
use DataTables\Tools\Functions;
use DataTables\Tools\Validator;
use InvalidArgumentException;

/**
 * Class OptionsOption
 *
 * @author   Allan Carvalho <allan.m.carvalho@outlook.com>
 * @license  MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
 * @link     https://github.com/allanmcarvalho/cakephp-datatables
 */
final class OptionsOption extends ChildOptionAbstract {

    const ALLOWED_PAGING_TYPES = [
        'numbers',
        'simple',
        'simple_numbers',
        'full',
        'full_numbers',
        'first_last_numbers',
    ];

    /**
     * @var array
     * @inheritDoc
     */
    protected $_config = [
        'deferLoading' => null,
        'destroy' => false,
        'displayStart' => 0,
        'dom' => 'lfrtip',
        'lengthMenu' => [10, 25, 50, 100],
        'order' => [[0, 'asc']],
        'orderCellsTop' => false,
        'orderClasses' => true,
        'orderFixed' => null,
        'orderMulti' => true,
        'pageLength' => 10,
        'pagingType' => 'simple_numbers',
        'renderer' => null,
        'retrieve' => false,
        'rowId' => 'DT_RowId',
        'scrollCollapse' => false,
        'search' => [
            'caseInsensitive' => true,
            'regex' => false,
            'search' => null,
            'smart' => true,
        ],
        'searchCols' => null,
        'searchDelay' => null,
        'stateDuration' => 7200,
        'stripeClasses' => null,
        'tabIndex' => 0,
    ];

    /**
     * Getter method.
     * When using server-side processing, the default mode of operation for DataTables is to simply throw away any data
     * that currently exists in the table and make a request to the server to get the first page of data to display.
     * This is fine for an empty table, but if you already have the first page of data displayed in the plain HTML, it
     * is a waste of resources. As such, this option exists to allow you to instruct DataTables to not make that
     * initial request, rather it will use the data already on the page (no sorting etc will be applied to it).
     *
     * deferLoading is used to indicate that deferred loading is required, but it is also used to tell DataTables how
     * many records there are in the full table (allowing the information element and pagination to be displayed
     * correctly). In the case where a filtering is applied to the table on initial load, this can be indicated by
     * giving the parameter as an array, where the first element is the number of records available after filtering and
     * the second element is the number of records without filtering (allowing the table information element to be
     * shown correctly).
     *
     * Note that this option only has effect when serverSide is enabled. It does not have any effect when using
     * client-side processing.
     *
     * Types:
     *  - integer: When given as an integer, this enables deferred loading, and instructs DataTables as to how many
     *    items are in the full data set.
     *  - array: As an array, this also enables deferred loading, while the first data index tells DataTables how many
     *    rows are in the filtered result set, and the second how many in the full data set without filtering applied.
     *
     * @link https://datatables.net/reference/option/deferLoading
     * @return int|array
     */
    public function getDeferLoading() {
        return $this->_getConfig('deferLoading');
    }

    /**
     * Setter method.
     * When using server-side processing, the default mode of operation for DataTables is to simply throw away any data
     * that currently exists in the table and make a request to the server to get the first page of data to display.
     * This is fine for an empty table, but if you already have the first page of data displayed in the plain HTML, it
     * is a waste of resources. As such, this option exists to allow you to instruct DataTables to not make that
     * initial request, rather it will use the data already on the page (no sorting etc will be applied to it).
     *
     * deferLoading is used to indicate that deferred loading is required, but it is also used to tell DataTables how
     * many records there are in the full table (allowing the information element and pagination to be displayed
     * correctly). In the case where a filtering is applied to the table on initial load, this can be indicated by
     * giving the parameter as an array, where the first element is the number of records available after filtering and
     * the second element is the number of records without filtering (allowing the table information element to be
     * shown correctly).
     *
     * Note that this option only has effect when serverSide is enabled. It does not have any effect when using
     * client-side processing.
     *
     * Types:
     *  - integer: When given as an integer, this enables deferred loading, and instructs DataTables as to how many
     *    items are in the full data set.
     *  - array: As an array, this also enables deferred loading, while the first data index tells DataTables how many
     *    rows are in the filtered result set, and the second how many in the full data set without filtering applied.
     *
     * @param int|array $deferLoading
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/deferLoading
     */
    public function setDeferLoading($deferLoading): MainOption {
        $type = getType($deferLoading);
        if (!in_array($type, ['array', 'integer'])) {
            throw new FatalErrorException("You must use only integer or array types on \$deferLoading. Found: '$type'.");
        }
        if ($type === 'array') {
            Validator::getInstance()->checkArraySizeOrFail($deferLoading, 2);
            Validator::getInstance()->checkKeysValueTypesOrFail($deferLoading, 'integer', 'integer', '$deferLoading');
        }
        $this->_setConfig('deferLoading', $deferLoading);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * Initialise a new DataTable as usual, but if there is an existing DataTable which matches the selector, it will
     * be destroyed and replaced with the new table. This can be useful if you want to change a property of the table
     * which cannot be altered through the API.
     *
     * Note that if you are not changing the configuration of the table, but just altering the data displayed by the
     * table, it is far more efficient to use the ajax.reload() method (or rows.add() etc).
     *
     * @link https://datatables.net/reference/option/destroy
     * @return bool
     */
    public function isDestroy(): bool {
        return (bool)$this->_getConfig('destroy');
    }

    /**
     * Setter method.
     * Initialise a new DataTable as usual, but if there is an existing DataTable which matches the selector, it will
     * be destroyed and replaced with the new table. This can be useful if you want to change a property of the table
     * which cannot be altered through the API.
     *
     * Note that if you are not changing the configuration of the table, but just altering the data displayed by the
     * table, it is far more efficient to use the ajax.reload() method (or rows.add() etc).
     *
     * @param bool $destroy
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/destroy
     */
    public function setDestroy(bool $destroy): MainOption {
        $this->_setConfig('destroy', $destroy);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * Define the starting point for data display when using DataTables with pagination (paging).
     *
     * Note that this parameter is the number of records (counting from 0), rather than the page number, so if you have
     * 10 records per page and want to start on the third page, it should be 20 rather than 2 or 3.
     *
     * @link https://datatables.net/reference/option/displayStart
     * @return int
     */
    public function getDisplayStart(): int {
        return (int)$this->_getConfig('displayStart');
    }

    /**
     * Setter method.
     * Define the starting point for data display when using DataTables with pagination (paging).
     *
     * Note that this parameter is the number of records (counting from 0), rather than the page number, so if you have
     * 10 records per page and want to start on the third page, it should be 20 rather than 2 or 3.
     *
     * @param int $displayStart
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/displayStart
     */
    public function setDisplayStart(int $displayStart): MainOption {
        $this->_setConfig('displayStart', $displayStart);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * DataTables will add a number of elements around the table to both control the table and show additional
     * information about it. The position of these elements on screen are controlled by a combination of their order in
     * the document (DOM) and the CSS applied to the elements. This parameter is used to control their ordering and
     * additional mark-up surrounding them in the DOM.
     *
     * Each table control element in DataTables has a single letter associated with it, and that letter it used in this
     * dom configuration option to indicate where that element will appear in the document order.
     *
     * The built-in table control elements in DataTables are:
     *  - l - length changing input control
     *  - f - filtering input
     *  - t - The table!
     *  - i - Table information summary
     *  - p - pagination control
     *  - r - processing display element
     *
     * @link https://datatables.net/reference/option/dom
     * @return string
     */
    public function getDom(): string {
        return (string)$this->_getConfig('dom');
    }

    /**
     * Setter method.
     * Define the starting point for data display when using DataTables with pagination (paging).
     *
     * Note that this parameter is the number of records (counting from 0), rather than the page number, so if you have
     * 10 records per page and want to start on the third page, it should be 20 rather than 2 or 3.
     *
     * @param string $dom
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/dom
     */
    public function setDom(string $dom): MainOption {
        $this->_setConfig('dom', $dom);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * DataTables will add a number of elements around the table to both control the table and show additional
     * information about it. The position of these elements on screen are controlled by a combination of their order in
     * the document (DOM) and the CSS applied to the elements. This parameter is used to control their ordering and
     * additional mark-up surrounding them in the DOM.
     *
     * Each table control element in DataTables has a single letter associated with it, and that letter it used in this
     * dom configuration option to indicate where that element will appear in the document order.
     *
     * The built-in table control elements in DataTables are:
     *  - l - length changing input control
     *  - f - filtering input
     *  - t - The table!
     *  - i - Table information summary
     *  - p - pagination control
     *  - r - processing display element
     *
     * @link https://datatables.net/reference/option/lengthMenu
     * @return array
     */
    public function getLengthMenu(): array {
        return $this->_getConfig('lengthMenu');
    }

    /**
     * Setter method.
     * This parameter allows you to readily specify the entries in the length drop down select list that DataTables
     * shows when pagination is enabled. It can be either:
     *  - 1D array of integer values which will be used for both the displayed option and the value to use for the
     *    display length, or
     *  - 2D array which will use the first inner array as the page length values and the second inner array as the
     *    displayed options. This is useful for language strings such as 'All').
     *
     * The page length values must always be integer values > 0, with the sole exception of -1. When -1 is used as a
     * value this tells DataTables to disable pagination (i.e. display all rows).
     *
     * Note that the pageLength property will be automatically set to the first value given in this array, unless
     * pageLength is also provided.
     *
     * @param array $lengthMenu
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/lengthMenu
     */
    public function setLengthMenu(array $lengthMenu): MainOption {
        if (count($lengthMenu) === 2 && is_array($lengthMenu[Functions::getInstance()->arrayKeyFirst($lengthMenu)]) && is_array($lengthMenu[Functions::getInstance()->arrayKeyLast($lengthMenu)])) {
            Validator::getInstance()->checkKeysValueTypesOrFail($lengthMenu[Functions::getInstance()->arrayKeyFirst($lengthMenu)], 'integer', 'integer', '$lengthMenu[options]');
            Validator::getInstance()->checkKeysValueTypesOrFail($lengthMenu[Functions::getInstance()->arrayKeyLast($lengthMenu)], 'integer', ['integer', 'string'], '$lengthMenu[optionsLabel]');
            if (count($lengthMenu[Functions::getInstance()->arrayKeyFirst($lengthMenu)]) !== count($lengthMenu[Functions::getInstance()->arrayKeyLast($lengthMenu)])) {
                throw new FatalErrorException('$lengthMenu[options] and $lengthMenu[optionsLabel] must have the same size.');
            }
        } else {
            Validator::getInstance()->checkKeysValueTypesOrFail($lengthMenu, 'integer', 'integer', '$lengthMenu');
        }
        $this->_setConfig('lengthMenu', $lengthMenu);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * If ordering is enabled (ordering), then DataTables will perform a first pass order during initialisation. Using
     * this parameter you can define which column(s) the order is performed upon, and the ordering direction. The order
     * must be an array of arrays, each inner array comprised of two elements:
     *  - Column index to order upon
     *  - Direction so order to apply (asc for ascending order or desc for descending order).
     *
     * This 2D array structure allows a multi-column order to be defined as the initial state should it be required.
     *
     * @link https://datatables.net/reference/option/order
     * @return array
     */
    public function getOrder(): array {
        return $this->_getConfig('order');
    }

    /**
     * Setter method.
     * If ordering is enabled (ordering), then DataTables will perform a first pass order during initialisation. Using
     * this parameter you can define which column(s) the order is performed upon, and the ordering direction. The order
     * must be an array of arrays, each inner array comprised of two elements:
     *  - Column index to order upon
     *  - Direction so order to apply (asc for ascending order or desc for descending order).
     *
     * This 2D array structure allows a multi-column order to be defined as the initial state should it be required.
     *
     * @param array $order
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/order
     */
    public function setOrder(array $order): MainOption {
        Validator::getInstance()->checkKeysValueTypesOrFail($order, 'integer', 'array', '$order');
        foreach ($order as $item) {
            Validator::getInstance()->checkArraySizeOrFail($item, 2, 'In setOrder($order) you must pass the index and order (asc or desc). Eg.: [0, \'asc\'].');
            Validator::getInstance()->checkKeysValueTypesOrFail($item, 'integer', ['integer', 'string'], '$order');
            $param1 = $item[Functions::getInstance()->arrayKeyFirst($item)];
            $param2 = $item[Functions::getInstance()->arrayKeyLast($item)];
            if (getType($param1) !== 'integer' || $param1 < 0) {
                throw new InvalidArgumentException("In setOrder(\$order) the index param must be a integer great or equals 0. Found: $param1.");
            }
            if (!in_array($param2, ['asc', 'desc'])) {
                throw new InvalidArgumentException("In setOrder(\$order) the order param must be asc or desc. Found: $param2.");
            }
        }
        $this->_setConfig('order', $order);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * Allows control over whether DataTables should use the top (true) unique cell that is found for a single column,
     * or the bottom (false - default) to attach the default order listener. This is useful when using complex headers.
     *
     * Consider for example the following HTML header:
     * <thead>
     *     <tr>
     *         <td rowspan="2">1</td>
     *         <td>2.1</td>
     *     </tr>
     *     <tr>
     *         <td>2.2</td>
     *     </tr>
     * </thead>
     *
     * In this case, when orderCellsTop is false (default) the cells 1 and 2.2 will have the order event listener
     * applied to them. If orderCellsTop is true then 1 and 2.1 will have the order event listeners applied to them.
     *
     * @link https://datatables.net/reference/option/orderCellsTop
     * @return bool
     */
    public function isOrderCellsTop(): bool {
        return (bool)$this->_getConfig('orderCellsTop');
    }

    /**
     * Setter method.
     * Allows control over whether DataTables should use the top (true) unique cell that is found for a single column,
     * or the bottom (false - default) to attach the default order listener. This is useful when using complex headers.
     *
     * Consider for example the following HTML header:
     * <thead>
     *     <tr>
     *         <td rowspan="2">1</td>
     *         <td>2.1</td>
     *     </tr>
     *     <tr>
     *         <td>2.2</td>
     *     </tr>
     * </thead>
     *
     * In this case, when orderCellsTop is false (default) the cells 1 and 2.2 will have the order event listener
     * applied to them. If orderCellsTop is true then 1 and 2.1 will have the order event listeners applied to them.
     *
     * @param bool $orderCellsTop
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/orderCellsTop
     */
    public function setOrderCellsTop(bool $orderCellsTop): MainOption {
        $this->_setConfig('orderCellsTop', $orderCellsTop);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * DataTables highlight the columns which are used to order the content in the table's body by adding a class to
     * the cells in that column, which in turn has CSS applied to those classes to highlight those cells.
     *
     * This is done by the addition of the classes sorting_1, sorting_2 and sorting_3 to the columns which are
     * currently being ordered on. The integer value indicates the level of sorting when mutli-column sorting. If more
     * than 3 columns are being ordered upon, the sorting_3 class is repeated.
     *
     * Please note that this feature can affect performance, particularly in old browsers and when there are a lot of
     * rows to be displayed as it is manipulating a large number of DOM elements. As such, this option is available as
     * a feature switch to allow this feature to be disabled with working with old browsers or large data sets.
     *
     * @link https://datatables.net/reference/option/orderClasses
     * @return bool
     */
    public function isOrderClasses(): bool {
        return (bool)$this->_getConfig('orderClasses');
    }

    /**
     * Setter method.
     * DataTables highlight the columns which are used to order the content in the table's body by adding a class to
     * the cells in that column, which in turn has CSS applied to those classes to highlight those cells.
     *
     * This is done by the addition of the classes sorting_1, sorting_2 and sorting_3 to the columns which are
     * currently being ordered on. The integer value indicates the level of sorting when mutli-column sorting. If more
     * than 3 columns are being ordered upon, the sorting_3 class is repeated.
     *
     * Please note that this feature can affect performance, particularly in old browsers and when there are a lot of
     * rows to be displayed as it is manipulating a large number of DOM elements. As such, this option is available as
     * a feature switch to allow this feature to be disabled with working with old browsers or large data sets.
     *
     * @param bool $orderClasses
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/orderClasses
     */
    public function setOrderClasses(bool $orderClasses): MainOption {
        $this->_setConfig('orderClasses', $orderClasses);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * The option works in tandem with the order option which provides an initial ordering state for the table which
     * can then be modified by the user clicking on column headings, while the ordering specified by this option will
     * always be applied to the table, regardless of user interaction.
     *
     * This fixed ordering can be applied before (pre) or after (post) the user's own ordering criteria using the two
     * different forms of this option (array or object) described below.
     *
     * The values that are used to describe the ordering conditions for the table are given as two element arrays:
     *  - Column index to order upon
     *  - Direction so order to apply (asc for ascending order or desc for descending order).
     *
     * It is also possible to give a set of nested arrays (i.e. arrays in arrays) to allow multi-column ordering to be
     * assigned.
     *
     * This option can be useful if you have a column (visible or hidden) which must always be sorted upon first - a
     * priority order or index column for example, or for grouping similar rows together.
     *
     * @link https://datatables.net/reference/option/orderFixed
     * @return array
     */
    public function getOrderFixed(): array {
        return $this->_getConfig('orderFixed');
    }

    /**
     * Setter method.
     * The option works in tandem with the order option which provides an initial ordering state for the table which
     * can then be modified by the user clicking on column headings, while the ordering specified by this option will
     * always be applied to the table, regardless of user interaction.
     *
     * This fixed ordering can be applied before (pre) or after (post) the user's own ordering criteria using the two
     * different forms of this option (array or object) described below.
     *
     * The values that are used to describe the ordering conditions for the table are given as two element arrays:
     *  - Column index to order upon
     *  - Direction so order to apply (asc for ascending order or desc for descending order).
     *
     * It is also possible to give a set of nested arrays (i.e. arrays in arrays) to allow multi-column ordering to be
     * assigned.
     *
     * This option can be useful if you have a column (visible or hidden) which must always be sorted upon first - a
     * priority order or index column for example, or for grouping similar rows together.
     *
     * @param array $orderFixed
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/orderFixed
     */
    public function setOrderFixed(array $orderFixed): MainOption {
        if (getType(Functions::getInstance()->arrayKeyFirst($orderFixed)) === 'string') {
            foreach ($orderFixed as $key => $objectItem) {
                if (!in_array($key, ['pre', 'post'])) {
                    throw new InvalidArgumentException("You must use only 'pre' or 'post' key for objects type. Found: $key.");
                }
                $this->checkOrderDefault($objectItem);
            }
        } else {
            $this->checkOrderDefault($orderFixed);
        }
        $this->_setConfig('orderFixed', $orderFixed);
        return $this->getMainOption();
    }

    /**
     * Check if orderFixed with default params are right.
     *
     * @param array $orderFixed
     * @return void
     */
    private function checkOrderDefault(array $orderFixed) {
        Validator::getInstance()->checkKeysValueTypesOrFail($orderFixed, ['integer', 'string'], 'array', '$orderFixed');
        foreach ($orderFixed as $item) {
            Validator::getInstance()->checkArraySizeOrFail($item, 2, "In \$orderFixed you must pass the index and after the order (asc or desc). Eg.: [0, 'asc'].");
            $param1 = $item[Functions::getInstance()->arrayKeyFirst($item)];
            $param2 = $item[Functions::getInstance()->arrayKeyLast($item)];
            if (getType($param1) !== 'integer' || $param1 < 0) {
                throw new InvalidArgumentException("In \$orderFixed the index param must be a integer great or equals 0. Found: $param1.");
            }
            if (!in_array($param2, ['asc', 'desc'])) {
                throw new InvalidArgumentException("In \$orderFixed the order param must be asc or desc. Found: $param2.");
            }
        }
    }

    /**
     * Checker method.
     * When ordering is enabled (ordering), by default DataTables allows users to sort multiple columns by shift
     * clicking upon the header cell for each column. Although this can be quite useful for users, it can also increase
     * the complexity of the order, potentiality increasing the processing time of ordering the data. Therefore, this
     * option is provided to allow this shift-click multiple column ability.
     *
     * Note that disabling this ability does not impede your ability as a developer to do multiple column ordering
     * using columns.orderData, order or order(), it just disallows the user from performing their own multi-column
     * order.
     *
     * @link https://datatables.net/reference/option/orderMulti
     * @return bool
     */
    public function isOrderMulti(): bool {
        return (bool)$this->_getConfig('orderMulti');
    }

    /**
     * Setter method.
     * When ordering is enabled (ordering), by default DataTables allows users to sort multiple columns by shift
     * clicking upon the header cell for each column. Although this can be quite useful for users, it can also increase
     * the complexity of the order, potentiality increasing the processing time of ordering the data. Therefore, this
     * option is provided to allow this shift-click multiple column ability.
     *
     * Note that disabling this ability does not impede your ability as a developer to do multiple column ordering
     * using columns.orderData, order or order(), it just disallows the user from performing their own multi-column
     * order.
     *
     * @param bool $orderMulti
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/orderMulti
     */
    public function setOrderMulti(bool $orderMulti): MainOption {
        $this->_setConfig('orderMulti', $orderMulti);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * Number of rows to display on a single page when using pagination.
     *
     * If lengthChange is feature enabled (it is by default) then the end user will be able to override the value set
     * here to a custom setting using a pop-up menu (see lengthMenu).
     *
     * @link https://datatables.net/reference/option/pageLength
     * @return int
     */
    public function getPageLength(): int {
        return (int)$this->_getConfig('pageLength');
    }

    /**
     * Setter method.
     * Number of rows to display on a single page when using pagination.
     *
     * If lengthChange is feature enabled (it is by default) then the end user will be able to override the value set
     * here to a custom setting using a pop-up menu (see lengthMenu).
     *
     * @param int $pageLength
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/pageLength
     */
    public function setPageLength(int $pageLength): MainOption {
        if ($pageLength <= 0) {
            throw new InvalidArgumentException("\$pageLength must be a positive integer number. Found: $pageLength.");
        }
        $this->_setConfig('pageLength', $pageLength);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * The pagination option of DataTables will display a pagination control below the table (by default, its position
     * can be changed using dom and CSS) with buttons that the end user can use to navigate the pages of the table.
     * Which buttons are shown in the pagination control are defined by the option given here.
     *
     * DataTables has six built-in paging button arrangements:
     *  - numbers - Page number buttons only (1.10.8)
     *  - simple - 'Previous' and 'Next' buttons only
     *  - simple_numbers - 'Previous' and 'Next' buttons, plus page numbers
     *  - full - 'First', 'Previous', 'Next' and 'Last' buttons
     *  - full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
     *  - first_last_numbers - 'First' and 'Last' buttons, plus page numbers
     *  - Further methods can be added using plug-ins.
     *
     * @link https://datatables.net/reference/option/pagingType
     * @return string
     */
    public function getPagingType(): string {
        return (string)$this->_getConfig('pagingType');
    }

    /**
     * Setter method.
     * The pagination option of DataTables will display a pagination control below the table (by default, its position
     * can be changed using dom and CSS) with buttons that the end user can use to navigate the pages of the table.
     * Which buttons are shown in the pagination control are defined by the option given here.
     *
     * DataTables has six built-in paging button arrangements:
     *  - numbers - Page number buttons only (1.10.8)
     *  - simple - 'Previous' and 'Next' buttons only
     *  - simple_numbers - 'Previous' and 'Next' buttons, plus page numbers
     *  - full - 'First', 'Previous', 'Next' and 'Last' buttons
     *  - full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
     *  - first_last_numbers - 'First' and 'Last' buttons, plus page numbers
     *  - Further methods can be added using plug-ins.
     *
     * @param string $pagingType
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/pagingType
     */
    public function setPagingType(string $pagingType): MainOption {
        if (!in_array($pagingType, static::ALLOWED_PAGING_TYPES)) {
            $allowedString = str_replace(' and ', ' or ', Text::toList(static::ALLOWED_PAGING_TYPES));
            throw new InvalidArgumentException("You must use one of $allowedString. Found: $pagingType.");
        }
        $this->_setConfig('pagingType', $pagingType);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * DataTables adds complex components to your HTML page, such as the pagination control. The business logic used to
     * calculate what information should be displayed (what buttons in the case of the pagination buttons) is core to
     * DataTables and generally doesn't vary how the buttons are actually displayed based on the styling requirements
     * of the page. For example the pagination buttons might be displayed as li elements in a ul list, or simply as a
     * collection of a buttons.
     *
     * This ability to use different renderers, while maintaining the same core business logic, is fundamental to how
     * DataTables provides integration options for CSS frameworks such as Bootstrap, Foundation and jQuery UI,
     * customising the HTML it uses to fit the requirements of each framework.
     *
     * This parameter controls which renderers will be used. The value given will be used if such a renderer exists,
     * otherwise the default renderer will be used. Additional renderers can be added by plug-ins.
     *
     * DataTables currently supports two different types of renderers:
     *  - header - header cell renderer
     *  - pageButton - pagination buttons
     *
     * This list will likely expand significantly in future versions of DataTables!
     *
     * @link https://datatables.net/reference/option/renderer
     * @return string|array
     */
    public function getRenderer() {
        return $this->_getConfig('renderer');
    }

    /**
     * Setter method.
     * DataTables adds complex components to your HTML page, such as the pagination control. The business logic used to
     * calculate what information should be displayed (what buttons in the case of the pagination buttons) is core to
     * DataTables and generally doesn't vary how the buttons are actually displayed based on the styling requirements
     * of the page. For example the pagination buttons might be displayed as li elements in a ul list, or simply as a
     * collection of a buttons.
     *
     * This ability to use different renderers, while maintaining the same core business logic, is fundamental to how
     * DataTables provides integration options for CSS frameworks such as Bootstrap, Foundation and jQuery UI,
     * customising the HTML it uses to fit the requirements of each framework.
     *
     * This parameter controls which renderers will be used. The value given will be used if such a renderer exists,
     * otherwise the default renderer will be used. Additional renderers can be added by plug-ins.
     *
     * DataTables currently supports two different types of renderers:
     *  - header - header cell renderer
     *  - pageButton - pagination buttons
     *
     * This list will likely expand significantly in future versions of DataTables!
     *
     * @param string|array $renderer
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/renderer
     */
    public function setRenderer($renderer): MainOption {
        $rendererType = getType($renderer);
        if (!in_array($rendererType, ['string', 'array'])) {
            throw new InvalidArgumentException("\$renderer must be a string or array. Found: $rendererType.");
        }
        if (is_array($renderer)) {
            Validator::getInstance()->checkKeysValueTypesOrFail($renderer, 'string', 'string', '$renderer');
            foreach ($renderer as $key => $item) {
                if (!in_array($key, ['header', 'pageButton'])) {
                    throw new InvalidArgumentException("You can user only 'header' and/or 'pageButton'. Found: $key.");
                }
            }
        }
        $this->_setConfig('renderer', $renderer);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * Retrieve the DataTables object for the given selector. Note that if the table has already been initialised, this
     * parameter will cause DataTables to simply return the object that has already been set up - it will not take
     * account of any changes you might have made to the initialisation object passed to DataTables (setting this
     *
     * The destroy option can be used to reinitialise a table with different options if required.
     *
     * @link https://datatables.net/reference/option/retrieve
     * @return bool
     */
    public function isRetrieve(): bool {
        return (bool)$this->_getConfig('retrieve');
    }

    /**
     * Setter method.
     * Retrieve the DataTables object for the given selector. Note that if the table has already been initialised, this
     * parameter will cause DataTables to simply return the object that has already been set up - it will not take
     * account of any changes you might have made to the initialisation object passed to DataTables (setting this
     *
     * The destroy option can be used to reinitialise a table with different options if required.
     *
     * @param bool $retrieve
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/retrieve
     */
    public function setRetrieve(bool $retrieve): MainOption {
        $this->_setConfig('retrieve', $retrieve);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * It can often be useful to have a id attribute on each tr element in a DataTable for row selection and data
     * source identification, particularly when using events.
     *
     * DataTables will attempt to automatically read an id value from the data source for each row using the property
     * defined by this option. By default it is DT_RowId but can be set to any other name. As with columns.data it can
     * also read from a nested JSON data source by using Javascript dotted object notation (e.g. DT_RowId: 'image.id').
     *
     * If no id value for the row is found, the id property will not be automatically set.
     *
     * Any row id values that are given in the data source should match the HTML specification for what values it can
     * take:
     *  - The value must be unique amongst all the IDs in the element's home subtree and must contain at least one
     *    character. The value must not contain any space characters.
     *
     * You may also wish to consider the CSS 2.1 specification of an identifier which is more restrictive than HTML5's
     * and will provide maximum compatibility with jQuery:
     *  - identifiers (including element names, classes, and IDs in selectors) can contain only the characters
     *    [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they
     *    cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped
     *    characters and any ISO 10646 character as a numeric code.
     *
     * @link https://datatables.net/reference/option/rowId
     * @return string
     */
    public function getRowId(): string {
        return (string)$this->_getConfig('rowId');
    }

    /**
     * Setter method.
     * It can often be useful to have a id attribute on each tr element in a DataTable for row selection and data
     * source identification, particularly when using events.
     *
     * DataTables will attempt to automatically read an id value from the data source for each row using the property
     * defined by this option. By default it is DT_RowId but can be set to any other name. As with columns.data it can
     * also read from a nested JSON data source by using Javascript dotted object notation (e.g. DT_RowId: 'image.id').
     *
     * If no id value for the row is found, the id property will not be automatically set.
     *
     * Any row id values that are given in the data source should match the HTML specification for what values it can
     * take:
     *  - The value must be unique amongst all the IDs in the element's home subtree and must contain at least one
     *    character. The value must not contain any space characters.
     *
     * You may also wish to consider the CSS 2.1 specification of an identifier which is more restrictive than HTML5's
     * and will provide maximum compatibility with jQuery:
     *  - identifiers (including element names, classes, and IDs in selectors) can contain only the characters
     *    [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they
     *    cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped
     *    characters and any ISO 10646 character as a numeric code.
     *
     * @param string $rowId
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/rowId
     */
    public function setRowId(string $rowId): MainOption {
        $this->_setConfig('rowId', $rowId);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * When vertical (y) scrolling is enabled through the use of the scrollY option, DataTables will force the height
     * of the table's viewport to the given height at all times (useful for layout). However, this can look odd when
     * filtering data down to a small data set, and the footer is left "floating" further down. This parameter (when
     * enabled) will cause DataTables to collapse the table's viewport when the result set fits within the given Y
     * height.
     *
     * @link https://datatables.net/reference/option/scrollCollapse
     * @return bool
     */
    public function isScrollCollapse(): bool {
        return (bool)$this->_getConfig('scrollCollapse');
    }

    /**
     * Setter method.
     * When vertical (y) scrolling is enabled through the use of the scrollY option, DataTables will force the height
     * of the table's viewport to the given height at all times (useful for layout). However, this can look odd when
     * filtering data down to a small data set, and the footer is left "floating" further down. This parameter (when
     * enabled) will cause DataTables to collapse the table's viewport when the result set fits within the given Y
     * height.
     *
     * @param bool $scrollCollapse
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/scrollCollapse
     */
    public function setScrollCollapse(bool $scrollCollapse): MainOption {
        $this->_setConfig('scrollCollapse', $scrollCollapse);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * Flag to indicate if the filtering should be case insensitive or not.
     *
     * @link https://datatables.net/reference/option/search.caseInsensitive
     * @return bool
     */
    public function isSearchCaseInsensitive(): bool {
        return (bool)$this->_getConfig('search.caseInsensitive');
    }

    /**
     * Setter method.
     * Flag to indicate if the filtering should be case insensitive or not.
     *
     * @param bool $caseInsensitive
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/search.caseInsensitive
     */
    public function setSearchCaseInsensitive(bool $caseInsensitive): MainOption {
        $this->_setConfig('search.caseInsensitive', $caseInsensitive);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * Regular expressions can be used to build fantastically complex filtering terms, but also it is perfectly valid
     * for users to enter characters such as * into the filter, so a decision needs to be made if you wish to escape
     * regular expression special characters or not. This option controls that ability in DataTables.
     *
     * It is simply a flag to indicate if the search term should be interpreted as a regular expression (true) or not
     * (false) and therefore and special regex characters escaped.
     *
     * @link https://datatables.net/reference/option/search.regex
     * @return bool
     */
    public function isSearchRegex(): bool {
        return (bool)$this->_getConfig('search.regex');
    }

    /**
     * Setter method.
     * Regular expressions can be used to build fantastically complex filtering terms, but also it is perfectly valid
     * for users to enter characters such as * into the filter, so a decision needs to be made if you wish to escape
     * regular expression special characters or not. This option controls that ability in DataTables.
     *
     * It is simply a flag to indicate if the search term should be interpreted as a regular expression (true) or not
     * (false) and therefore and special regex characters escaped.
     *
     * @param bool $regex
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/search.regex
     */
    public function setSearchRegex(bool $regex): MainOption {
        $this->_setConfig('search.regex', $regex);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * Search term that should be applied to the table.
     *
     * @link https://datatables.net/reference/option/search.search
     * @return string
     */
    public function getSearchSearch(): string {
        return (string)$this->_getConfig('search.search');
    }

    /**
     * Setter method.
     * Search term that should be applied to the table.
     *
     * @param string $search
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/search.search
     */
    public function setSearchSearch(string $search): MainOption {
        $this->_setConfig('search.search', $search);
        return $this->getMainOption();
    }

    /**
     * Checker method.
     * DataTables' built-in filtering is "smart" in that it breaks the user's input into individual words and then
     * matches those words in any position and in any order in the table (rather than simple doing a simple string
     * compare).
     *
     * Although this can significantly enhance usability of the filtering feature, it uses a complex regular expression
     * to perform this task, and as such it can interfere with a custom regular expression input if you enable that
     * option (search.regex). As such, this option is provided to disable this smart filtering ability.
     *
     * @link https://datatables.net/reference/option/search.smart
     * @return bool
     */
    public function isSearchSmart(): bool {
        return (bool)$this->_getConfig('search.smart');
    }

    /**
     * Setter method.
     * DataTables' built-in filtering is "smart" in that it breaks the user's input into individual words and then
     * matches those words in any position and in any order in the table (rather than simple doing a simple string
     * compare).
     *
     * Although this can significantly enhance usability of the filtering feature, it uses a complex regular expression
     * to perform this task, and as such it can interfere with a custom regular expression input if you enable that
     * option (search.regex). As such, this option is provided to disable this smart filtering ability.
     *
     * @param bool $smart
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/search.smart
     */
    public function setSearchSmart(bool $smart): MainOption {
        $this->_setConfig('search.smart', $smart);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * Basically the same as the search option, but in this case for individual columns, rather than the global filter,
     * this option defined the filtering to apply to the table during initialisation.
     *
     * The array must be of the same size as the number of columns, and each element be an object with the parameters
     * search, regex (optional, default false) and smart (optional, default true). null is also accepted and the
     * default will be used. See the search documentation for more information on these parameters.
     *
     * @link https://datatables.net/reference/option/searchCols
     * @return array
     */
    public function getSearchCols(): array {
        return $this->_getConfig('searchCols');
    }

    /**
     * Setter method.
     * Basically the same as the search option, but in this case for individual columns, rather than the global filter,
     * this option defined the filtering to apply to the table during initialisation.
     *
     * The array must be of the same size as the number of columns, and each element be an object with the parameters
     * search, regex (optional, default false) and smart (optional, default true). null is also accepted and the
     * default will be used. See the search documentation for more information on these parameters.
     *
     * @param array $searchCols
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/searchCols
     */
    public function setSearchCols(array $searchCols): MainOption {
        Validator::getInstance()->checkKeysValueTypesOrFail($searchCols, ['integer'], ['array', 'NULL'], '$searchCols');
        foreach ($searchCols as $searchCol) {
            if ($searchCol !== null) {
                foreach ($searchCol as $key => $item) {
                    $itemType = getType($item);
                    if (!in_array($key, ['caseInsensitive', 'regex', 'search', 'smart'])) {
                        throw new InvalidArgumentException("You can use only 'caseInsensitive', 'regex', 'search' or 'smart' param. Found: $key.");
                    } elseif (in_array($key, ['caseInsensitive', 'regex', 'smart']) && $itemType !== 'boolean') {
                        throw new InvalidArgumentException("$key param must be a boolean. Found: $itemType.");
                    } elseif ($key === 'search' && $itemType !== 'string') {
                        throw new InvalidArgumentException("$key param must be a string. Found: $itemType.");
                    }
                }
            }
        }
        $this->_setConfig('searchCols', $searchCols);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * The built-in DataTables global search (by default at the top right of every DataTable) will instantly search the
     * table on every keypress when in client-side processing mode and reduce the search call frequency automatically
     * to 400mS when in server-side processing mode. This call frequency (throttling) can be controlled using the
     * searchDelay parameter for both client-side and server-side processing.
     *
     * Being able to control the call frequency has a number of uses:
     *  - Older browsers and slower computers can have their processing load reduced by reducing the search frequency
     *  - Fewer table redraws while searching can be less distracting for the user
     *  - Reduce the load on the server when using server-side processing by making fewer calls
     *  - Conversely, you can speed up the search when using server-side processing by reducing the default of 400mS to
     *    instant (0).
     *
     * As with many other parts of DataTables, it is up to yourself how you configure it to suit your needs!
     *
     * The value given for searchDelay is in milliseconds (mS).
     *
     * Please note that this option effects only the built in global search box that DataTables provides. It does not
     * effect the search() or column().search() methods at all. If you wish to be able to throttle calls to those API
     * methods use the utility method $.fn.dataTable.util.throttle().
     *
     * @link https://datatables.net/reference/option/searchDelay
     * @return int
     */
    public function getSearchDelay(): int {
        return (int)$this->_getConfig('searchDelay');
    }

    /**
     * Setter method.
     * The built-in DataTables global search (by default at the top right of every DataTable) will instantly search the
     * table on every keypress when in client-side processing mode and reduce the search call frequency automatically
     * to 400mS when in server-side processing mode. This call frequency (throttling) can be controlled using the
     * searchDelay parameter for both client-side and server-side processing.
     *
     * Being able to control the call frequency has a number of uses:
     *  - Older browsers and slower computers can have their processing load reduced by reducing the search frequency
     *  - Fewer table redraws while searching can be less distracting for the user
     *  - Reduce the load on the server when using server-side processing by making fewer calls
     *  - Conversely, you can speed up the search when using server-side processing by reducing the default of 400mS to
     *    instant (0).
     *
     * As with many other parts of DataTables, it is up to yourself how you configure it to suit your needs!
     *
     * The value given for searchDelay is in milliseconds (mS).
     *
     * Please note that this option effects only the built in global search box that DataTables provides. It does not
     * effect the search() or column().search() methods at all. If you wish to be able to throttle calls to those API
     * methods use the utility method $.fn.dataTable.util.throttle().
     *
     * @param int $searchDelay
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/searchDelay
     */
    public function setSearchDelay(int $searchDelay): MainOption {
        if ($searchDelay < 0) {
            throw new InvalidArgumentException("\$searchDelay must be a positive integer number. Found: $searchDelay.");
        }
        $this->_setConfig('searchDelay', $searchDelay);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * Duration for which the saved state information is considered valid. After this period has elapsed the state will
     * be returned to the default.
     *
     * This option is also used to indicate to DataTables if localStorage or sessionStorage should be used for storing
     * the table's state. When set to -1 sessionStorage will be used, while for 0 or greater localStorage will be used.
     *
     * The difference between the two storage APIs is that sessionStorage retains data only for the current session
     * (i..e the current browser window). For more information on these two HTML APIs please refer to the Mozilla
     * Storage documentation.
     *
     * Please note that the value is given in seconds. The value 0 is a special value as it indicates that the state
     * can be stored and retrieved indefinitely with no time limit.
     *
     * @link https://datatables.net/reference/option/stateDuration
     * @return int
     */
    public function getStateDuration(): int {
        return (int)$this->_getConfig('stateDuration');
    }

    /**
     * Setter method.
     * Duration for which the saved state information is considered valid. After this period has elapsed the state will
     * be returned to the default.
     *
     * This option is also used to indicate to DataTables if localStorage or sessionStorage should be used for storing
     * the table's state. When set to -1 sessionStorage will be used, while for 0 or greater localStorage will be used.
     *
     * The difference between the two storage APIs is that sessionStorage retains data only for the current session
     * (i..e the current browser window). For more information on these two HTML APIs please refer to the Mozilla
     * Storage documentation.
     *
     * Please note that the value is given in seconds. The value 0 is a special value as it indicates that the state
     * can be stored and retrieved indefinitely with no time limit.
     *
     * @param int $stateDuration
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/stateDuration
     */
    public function setStateDuration(int $stateDuration): MainOption {
        if ($stateDuration <= 0) {
            throw new InvalidArgumentException("\$stateDuration must be a positive integer number. Found: $stateDuration.");
        }
        $this->_setConfig('stateDuration', $stateDuration);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * An array of CSS classes that should be applied to displayed rows, in sequence. This array may be of any length,
     * and DataTables will apply each class sequentially, looping when required.
     *
     * Note that by default this option will take the values determined by the $.fn.dataTable.ext.classes.stripe*
     * options (these are odd and even by default).
     *
     * @link https://datatables.net/reference/option/stripeClasses
     * @return array
     */
    public function getStripeClasses(): array {
        return $this->_getConfig('stripeClasses');
    }

    /**
     * Setter method.
     * An array of CSS classes that should be applied to displayed rows, in sequence. This array may be of any length,
     * and DataTables will apply each class sequentially, looping when required.
     *
     * Note that by default this option will take the values determined by the $.fn.dataTable.ext.classes.stripe*
     * options (these are odd and even by default).
     *
     * @param array $stripeClasses
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/stripeClasses
     */
    public function setStripeClasses(array $stripeClasses): MainOption {
        Validator::getInstance()->checkKeysValueTypesOrFail($stripeClasses, 'integer', 'string', '$stripeClasses');
        $this->_setConfig('stripeClasses', $stripeClasses);
        return $this->getMainOption();
    }

    /**
     * Getter method.
     * By default DataTables allows keyboard navigation of the table (sorting, paging, and filtering) by adding a
     * tabindex attribute to the required elements. This allows the end user to tab through the controls and press the
     * enter key to activate them, allowing the table controls to be accessible without a mouse.
     *
     * The default tabindex is 0, meaning that the tab follows the flow of the document. You can overrule this using
     * this parameter if you wish. Use a value of -1 to disable built-in keyboard navigation, although this is not
     * recommended for accessibility reasons.
     *
     * @link https://datatables.net/reference/option/tabIndex
     * @return int
     */
    public function getTabIndex(): int {
        return (int)$this->_getConfig('tabIndex');
    }

    /**
     * Setter method.
     * By default DataTables allows keyboard navigation of the table (sorting, paging, and filtering) by adding a
     * tabindex attribute to the required elements. This allows the end user to tab through the controls and press the
     * enter key to activate them, allowing the table controls to be accessible without a mouse.
     *
     * The default tabindex is 0, meaning that the tab follows the flow of the document. You can overrule this using
     * this parameter if you wish. Use a value of -1 to disable built-in keyboard navigation, although this is not
     * recommended for accessibility reasons.
     *
     * @param int $tabIndex
     * @return \DataTables\Table\Option\MainOption
     * @link https://datatables.net/reference/option/tabIndex
     */
    public function setTabIndex(int $tabIndex): MainOption {
        $this->_setConfig('tabIndex', $tabIndex);
        return $this->getMainOption();
    }

}