├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── composer.json
├── config
├── app.php
└── paths.php
├── docs
├── CONTRIBUTING.md
└── README.md
├── phpunit.xml.dist
├── src
├── Controller
│ └── AppController.php
├── Plugin.php
├── StorageEngine
│ ├── CacheStorageEngine.php
│ └── StorageEngineInterface.php
├── Table
│ ├── Column.php
│ ├── Columns.php
│ ├── ConfigBundle.php
│ ├── Option
│ │ ├── CallBack
│ │ │ └── MainCallBack.php
│ │ ├── ChildOptionAbstract.php
│ │ ├── MainOption.php
│ │ ├── OptionAbstract.php
│ │ └── Section
│ │ │ ├── ColumnsOption.php
│ │ │ ├── FeaturesOption.php
│ │ │ └── OptionsOption.php
│ ├── QueryBaseState.php
│ └── Tables.php
├── Tools
│ ├── Builder.php
│ ├── Functions.php
│ ├── Js.php
│ └── Validator.php
└── View
│ ├── Cell
│ └── DataTablesCell.php
│ └── Helper
│ └── DataTablesHelper.php
├── templates
├── cell
│ └── DataTables
│ │ └── table.php
└── twig
│ └── js
│ ├── bake
│ └── callback_created_cell.twig
│ └── functions
│ └── callback_created_cell.twig
├── tests
├── Fixture
│ ├── ArticlesFixture.php
│ └── UsersFixture.php
├── TestCase
│ ├── PluginTest.php
│ ├── StorageEngine
│ │ └── CacheStorageEngineTest.php
│ ├── Table
│ │ ├── ColumnTest.php
│ │ ├── ColumnsTest.php
│ │ ├── Option
│ │ │ ├── Callback
│ │ │ │ └── MainCallBackTest.php
│ │ │ ├── MainOptionTest.php
│ │ │ └── Section
│ │ │ │ ├── FeaturesOptionTest.php
│ │ │ │ └── OptionsOptionTest.php
│ │ └── TablesTest.php
│ ├── Tools
│ │ ├── FunctionsTest.php
│ │ ├── JsTest.php
│ │ └── ValidatorTest.php
│ └── View
│ │ ├── Cell
│ │ └── DataTablesCellTest.php
│ │ └── Helper
│ │ └── DataTablesHelperTest.php
├── bootstrap.php
├── phpstan.neon
└── test_app
│ ├── src
│ ├── Application.php
│ ├── Controller
│ │ └── AppController.php
│ ├── DataTables
│ │ └── Tables
│ │ │ └── CategoriesTables.php
│ └── View
│ │ └── AppView.php
│ └── templates
│ └── data_tables
│ └── Categories
│ └── main
│ └── callback_created_cell.twig
└── webroot
└── .gitkeep
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | /phpunit.xml
3 | /phpunit.phar
4 | /vendor
5 | /tmp/
6 | composer.phar
7 | .idea/
8 | config/Migrations/schema-dump-default.lock
9 | /.phpunit.result.cache
10 | schema-dump-default.lock
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.2
5 | - 7.4
6 |
7 | env:
8 | matrix:
9 | - DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test'
10 | global:
11 | - DEFAULT=1
12 |
13 | services:
14 | - postgresql
15 | - mysql
16 |
17 | matrix:
18 | fast_finish: true
19 |
20 | include:
21 | - php: 7.2
22 | env: DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test'
23 |
24 | - php: 7.3
25 | env: DB=sqlite db_dsn='sqlite:///:memory:'
26 |
27 | - php: 7.2
28 | env: PREFER_LOWEST=1
29 |
30 | - php: 7.3
31 | env: CODECOVERAGE=1 DEFAULT=0 DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test'
32 |
33 | - php: 7.3
34 | env: CHECKS=1 DEFAULT=1
35 |
36 | before_install:
37 | - phpenv config-rm xdebug.ini
38 |
39 | before_script:
40 | - if [[ $CHECKS != 1 ]]; then composer require --dev phpunit/phpunit:"^8.3"; fi
41 |
42 | - if [[ $PREFER_LOWEST != 1 ]]; then composer install --prefer-source --no-interaction; fi
43 | - if [[ $PREFER_LOWEST == 1 ]]; then composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction; fi
44 | - if [[ $PREFER_LOWEST == 1 ]]; then composer require --dev dereuromark/composer-prefer-lowest:dev-master; fi
45 |
46 | - if [[ $DB == 'mysql' ]]; then mysql -u root -e 'CREATE DATABASE cakephp_test;'; fi
47 | - if [[ $DB == 'pgsql' ]]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi
48 |
49 | script:
50 | - if [[ $DEFAULT == 1 ]]; then vendor/bin/phpunit; fi
51 | - if [[ $PREFER_LOWEST == 1 ]]; then vendor/bin/validate-prefer-lowest; fi
52 |
53 | - if [[ $CHECKS == 1 ]]; then composer phpstan-setup && composer phpstan; fi
54 | - if [[ $CHECKS == 1 ]]; then composer cs-check; fi
55 |
56 | - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml; fi
57 |
58 | after_success:
59 | - if [[ $CODECOVERAGE == 1 ]]; then bash <(curl -s https://codecov.io/bash); fi
60 |
61 | cache:
62 | directories:
63 | - $HOME/.composer/cache
64 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at allan.m.carvalho@outlook.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Allan Mariucci Carvalho
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP DataTables Plugin
2 | [](https://travis-ci.com/github/allanmcarvalho/cakephp-datatables)
3 | [](https://codecov.io/github/allanmcarvalho/cakephp-datatables?branch=master)
4 | [](https://codeclimate.com/github/allanmcarvalho/cakephp-datatables/maintainability)
5 | [](https://packagist.org/packages/allanmcarvalho/cakephp-datatables)
6 | [](https://img.shields.io/github/languages/code-size/allanmcarvalho/cakephp-datatables)
7 | [](https://php.net/)
8 | [](https://packagist.org/packages/allanmcarvalho/cakephp-datatables)
9 | [](https://packagist.org/packages/allanmcarvalho/cakephp-datatables)
10 | [](https://github.com/php-fig-rectified/fig-rectified-standards)
11 | [](https://github.com/phpstan/phpstan)
12 |
13 | This branch is for use with **CakePHP 4.0+**. For details see [version map](https://github.com/allanmcarvalho/cakephp-datatables/wiki#cakephp-version-map).
14 |
15 |
16 | This is a very simple and minimalistic (Automagic) implementation of
17 | [DataTables jQuery Plugin](https://datatables.net/) for CakePHP.
18 | If you need to create simple and basics DataTables tables without extremely hard configurations, this will be a very wise choice.
19 |
20 | Overall functionality is inspired and improved by one plugin from CakepPHP 2.x that isn't longer available.
21 |
22 | The plugin is an attempt to provide a basic, simple and friendly way to implement the
23 | DataTables library in order to be connected with CakePHP features and requirements.
24 |
25 | ## Installation and Usage
26 | See [Documentation](docs).
27 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "allanmcarvalho/cakephp-datatables",
3 | "type": "cakephp-plugin",
4 | "description": "DataTables plugin for CakePHP 4.x",
5 | "homepage": "https://github.com/allanmcarvalho/cakephp-datatables",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Allan Carvalho",
10 | "homepage": "https://wsssoftware.com.br",
11 | "role": "Maintainer"
12 | },
13 | {
14 | "name": "Contributors",
15 | "homepage": "https://github.com/allanmcarvalho/cakephp-datatables/graphs/contributors",
16 | "role": "Contributor"
17 | }
18 | ],
19 | "require": {
20 | "php": ">=7.2",
21 | "cakephp/cakephp": "^4.0",
22 | "twig/twig": "^3.0",
23 | "matthiasmullie/minify": "^1.3.50",
24 | "ext-json": "*"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "^8.0",
28 | "fig-r/psr2r-sniffer": "dev-master"
29 | },
30 | "support": {
31 | "source": "https://github.com/allanmcarvalho/cakephp-datatables"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "DataTables\\": "src/"
36 | }
37 | },
38 | "autoload-dev": {
39 | "psr-4": {
40 | "DataTables\\Test\\": "tests/",
41 | "Cake\\Test\\": "vendor/cakephp/cakephp/tests/",
42 | "TestApp\\": "tests/test_app/src/"
43 | }
44 | },
45 | "prefer-stable": true,
46 | "minimum-stability": "dev",
47 | "scripts": {
48 | "phpstan": "phpstan analyse -c tests/phpstan.neon -l 5 src/",
49 | "phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^0.12 && mv composer.backup composer.json",
50 | "test": "php phpunit.phar",
51 | "test-setup": "[ ! -f phpunit.phar ] && wget https://phar.phpunit.de/phpunit-8.5.2.phar && mv phpunit-8.5.2.phar phpunit.phar || true",
52 | "cs-check": "phpcs -p -s --standard=vendor/fig-r/psr2r-sniffer/PSR2R/ruleset.xml --ignore=/config/Migrations/ --extensions=php src/ tests/ config/",
53 | "cs-fix": "phpcbf -p --standard=vendor/fig-r/psr2r-sniffer/PSR2R/ruleset.xml --ignore=/config/Migrations/ --extensions=php src/ tests/ config/"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | declare(strict_types = 1);
12 |
13 | return [
14 | 'DataTables' => [
15 | 'StorageEngine' => [
16 | 'class' => \DataTables\StorageEngine\CacheStorageEngine::class,
17 | 'disableWhenDebugOn' => true,
18 | ],
19 | 'resources' => [
20 | 'templates' => ROOT . DS . 'templates' . DS . 'data_tables' . DS,
21 | 'twigCacheFolder' => CACHE . DS . 'data_tables' . DS . 'twig' . DS,
22 | ],
23 | 'Cache' => [
24 | '_data_tables_config_bundles_' => [
25 | 'className' => \Cake\Cache\Engine\FileEngine::class,
26 | 'prefix' => 'built_config_',
27 | 'path' => CACHE . DS . 'data_tables' . DS . 'config_bundles' . DS,
28 | 'serialize' => true,
29 | 'duration' => '+30 days',
30 | 'url' => env('CACHE_CAKECORE_URL', null),
31 | ],
32 | ],
33 | ],
34 | ];
35 |
--------------------------------------------------------------------------------
/config/paths.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | /*
13 | * Use the DS to separate the directories in other defines
14 | */
15 | if (!defined('DS')) {
16 | define('DS', DIRECTORY_SEPARATOR);
17 | }
18 |
19 | /*
20 | * These defines should only be edited if you have cake installed in
21 | * a directory layout other than the way it is distributed.
22 | * When using custom settings be sure to use the DS and do not add a trailing DS.
23 | */
24 |
25 | /*
26 | * The full path to the directory which holds "src", WITHOUT a trailing DS.
27 | */
28 | define('DATA_TABLES_ROOT', dirname(__DIR__));
29 |
30 | /*
31 | * The actual directory name for the application directory. Normally
32 | * named 'src'.
33 | */
34 | define('DATA_TABLES_APP_DIR', 'src');
35 |
36 | /*
37 | * Path to the application's directory.
38 | */
39 | define('DATA_TABLES_APP', DATA_TABLES_ROOT . DS . DATA_TABLES_APP_DIR . DS);
40 |
41 | /*
42 | * Path to the config directory.
43 | */
44 | define('DATA_TABLES_CONFIG', DATA_TABLES_ROOT . DS . 'config' . DS);
45 |
46 | /*
47 | * File path to the templates directory.
48 | *
49 | * To derive your templates from your webserver change this to:
50 | *
51 | * `define('WWW_ROOT', rtrim($_SERVER['DOCUMENT_ROOT'], DS) . DS);`
52 | */
53 | define('DATA_TABLES_TEMPLATES', DATA_TABLES_ROOT . DS . 'templates' . DS);
54 |
55 | /*
56 | * File path to the webroot directory.
57 | *
58 | * To derive your webroot from your webserver change this to:
59 | *
60 | * `define('WWW_ROOT', rtrim($_SERVER['DOCUMENT_ROOT'], DS) . DS);`
61 | */
62 | define('DATA_TABLES_WWW_ROOT', DATA_TABLES_ROOT . DS . 'webroot' . DS);
63 |
64 | /*
65 | * Path to the tests directory.
66 | */
67 | define('DATA_TABLES_TESTS', DATA_TABLES_ROOT . DS . 'tests' . DS);
68 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | I am looking forward to your contributions.
4 |
5 | There are a few guidelines that I need contributors to follow:
6 | * Coding standards (`composer cs-check` to check and `composer cs-fix` to fix)
7 | * Passing tests (`php phpunit.phar`)
8 |
9 |
10 |
11 | ## Updating Locale POT file
12 |
13 | Run this from your app dir to update the plugin's `datatables.pot` file:
14 | ```
15 | bin/cake i18n extract --plugin DataTables --extract-core=no --merge=no --overwrite
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP DataTables Plugin Documentation
2 |
3 |
4 | ## Installation
5 | ```
6 | composer require allanmcarvalho/cakephp-datatables
7 | ```
8 | Load the plugin in your `src/Application.php`'s bootstrap() using:
9 | ```php
10 | $this->addPlugin('DataTables');
11 | //OR
12 | $this->addPlugin(\DataTables\Plugin::class);
13 | ```
14 |
15 | It is also advised to have the `posix` PHP extension enabled.
16 |
17 |
18 | ## Configuration
19 |
20 | ### Under Construction!
21 |
22 | ## Contributing
23 |
24 | See [CONTRIBUTING.md](CONTRIBUTING.md).
25 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | tests/TestCase/
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | src/
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/Controller/AppController.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Controller;
13 |
14 | use App\Controller\AppController as BaseController;
15 |
16 | /**
17 | * Class AppController
18 | *
19 | * @author Allan Carvalho
20 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
21 | * @link https://github.com/allanmcarvalho/cakephp-datatables
22 | */
23 | class AppController extends BaseController {
24 |
25 | /**
26 | * @return void
27 | */
28 | public function initialize(): void {
29 | parent::initialize(); // TODO: Change the autogenerated stub
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/Plugin.php:
--------------------------------------------------------------------------------
1 |
10 | * @license MIT License https://github.com/allanmcarvalho/cakephp-data-renderer/blob/master/LICENSE
11 | * @link https://github.com/allanmcarvalho/cakephp-data-renderer
12 | */
13 | declare(strict_types = 1);
14 |
15 | namespace DataTables;
16 |
17 | require_once __DIR__ . DS . '..' . DS . 'config' . DS . 'paths.php';
18 |
19 | use Cake\Cache\Cache;
20 | use Cake\Core\BasePlugin;
21 | use Cake\Core\Configure;
22 | use Cake\Core\PluginApplicationInterface;
23 | use Cake\Error\FatalErrorException;
24 | use Cake\Utility\Hash;
25 |
26 | /**
27 | * Plugin for DataTables
28 | */
29 | class Plugin extends BasePlugin {
30 |
31 | /**
32 | * @return void
33 | */
34 | public function initialize(): void {
35 | parent::initialize();
36 | }
37 |
38 | /**
39 | * Load all the plugin configuration and bootstrap logic.
40 | *
41 | * The host application is provided as an argument. This allows you to load
42 | * additional plugin dependencies, or attach events.
43 | *
44 | * @param \Cake\Core\PluginApplicationInterface $app The host application
45 | * @return void
46 | */
47 | public function bootstrap(PluginApplicationInterface $app): void {
48 | $applicationDataTablesConfigs = Configure::read('DataTables', []);
49 | if (!is_array($applicationDataTablesConfigs)) {
50 | throw new FatalErrorException('DataTables config key must contain an array');
51 | }
52 | $applicationDataTablesConfigs = Configure::read('DataTables', []);
53 | Configure::load('DataTables.app', 'default', true);
54 | $pluginDataTablesConfigs = Configure::read('DataTables', []);
55 | Configure::write('DataTables', Hash::merge($pluginDataTablesConfigs, $applicationDataTablesConfigs));
56 | unset($applicationDataTablesConfigs);
57 | unset($pluginDataTablesConfigs);
58 | foreach (Configure::read('DataTables.Cache') as $cacheConfigName => $cacheConfig) {
59 | if (empty(Cache::getConfig($cacheConfigName))) {
60 | Cache::setConfig($cacheConfigName, $cacheConfig);
61 | }
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/StorageEngine/CacheStorageEngine.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\StorageEngine;
13 |
14 | use Cake\Cache\Cache;
15 | use DataTables\Table\ConfigBundle;
16 |
17 | /**
18 | * Class CacheStorageEngine
19 | *
20 | * @author Allan Carvalho
21 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
22 | * @link https://github.com/allanmcarvalho/cakephp-datatables
23 | */
24 | class CacheStorageEngine implements StorageEngineInterface {
25 |
26 | /**
27 | * @var string
28 | */
29 | private $_cacheConfigName = '_data_tables_config_bundles_';
30 |
31 | /**
32 | * CacheStorageEngine constructor.
33 | *
34 | * @param string|null $cacheConfigName
35 | */
36 | public function __construct(?string $cacheConfigName = null) {
37 | if (!empty($cacheConfigName)) {
38 | $this->_cacheConfigName = $cacheConfigName;
39 | }
40 | Cache::getConfigOrFail($this->_cacheConfigName);
41 | }
42 |
43 | /**
44 | * @inheritDoc
45 | */
46 | public function save(string $key, ConfigBundle $configBundle): bool {
47 | return Cache::write($key, $configBundle, '_data_tables_config_bundles_');
48 | }
49 |
50 | /**
51 | * @inheritDoc
52 | */
53 | public function exists(string $key): bool {
54 | return Cache::read($key, '_data_tables_config_bundles_') instanceof ConfigBundle;
55 | }
56 |
57 | /**
58 | * @inheritDoc
59 | */
60 | public function read(string $key): ?ConfigBundle {
61 | $configBundle = Cache::read($key, '_data_tables_config_bundles_');
62 | return ($configBundle instanceof ConfigBundle) ? $configBundle : null;
63 | }
64 |
65 | /**
66 | * @inheritDoc
67 | */
68 | public function delete(string $key): bool {
69 | return Cache::delete($key, '_data_tables_config_bundles_');
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/StorageEngine/StorageEngineInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\StorageEngine;
13 |
14 | use DataTables\Table\ConfigBundle;
15 |
16 | /**
17 | * Interface StorageEngineInterface
18 | *
19 | * @package DataTables\StorageEngine
20 | */
21 | interface StorageEngineInterface {
22 |
23 | /**
24 | * Create or replace if exists a ConfigBundle for a key.
25 | *
26 | * @param string $key A unique key that represent this bundle.
27 | * @param \DataTables\Table\ConfigBundle $configBundle A ConfigBundle instance.
28 | * @return bool True if the data was successfully saved, false on failure.
29 | */
30 | public function save(string $key, ConfigBundle $configBundle): bool;
31 |
32 | /**
33 | * Check a ConfigBundle exist for a key.
34 | *
35 | * @param string $key A unique key that represent this bundle.
36 | * @return bool True if the data exists, false if not.
37 | */
38 | public function exists(string $key): bool;
39 |
40 | /**
41 | * Read if a ConfigBundle for a key.
42 | *
43 | * @param string $key A unique key that represent this bundle.
44 | * @return \DataTables\Table\ConfigBundle|null ConfigBundle class if the data was successfully read, null on not found.
45 | */
46 | public function read(string $key): ?ConfigBundle;
47 |
48 | /**
49 | * Delete a ConfigBundle exist for a key.
50 | *
51 | * @param string $key A unique key that represent this bundle.
52 | * @return bool True if the data was successfully deleted, false on failure.
53 | */
54 | public function delete(string $key): bool;
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/Table/Column.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table;
13 |
14 | use Cake\Error\FatalErrorException;
15 | use Cake\Utility\Inflector;
16 | use Cake\Utility\Text;
17 | use DataTables\Tools\Validator;
18 | use InvalidArgumentException;
19 |
20 | /**
21 | * Class Column
22 | *
23 | * @author Allan Carvalho
24 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
25 | * @link https://github.com/allanmcarvalho/cakephp-datatables
26 | */
27 | final class Column {
28 |
29 | const TYPE_DATE = 'date';
30 | const TYPE_NUM = 'num';
31 | const TYPE_NUM_FMT = 'num-fmt';
32 | const TYPE_HTML_NUM = 'html-num';
33 | const TYPE_HTML_NUM_FMT = 'html-num-fmt';
34 | const TYPE_HTML = 'html';
35 | const TYPE_STRING = 'string';
36 | const VALID_TYPES = [
37 | self::TYPE_DATE,
38 | self::TYPE_NUM,
39 | self::TYPE_NUM_FMT,
40 | self::TYPE_HTML_NUM,
41 | self::TYPE_HTML_NUM_FMT,
42 | self::TYPE_HTML,
43 | self::TYPE_STRING,
44 | ];
45 |
46 | const DOM_TEXT = 'dom-text';
47 | const DOM_SELECT = 'dom-select';
48 | const DOM_CHECKBOX = 'dom-checkbox';
49 | const VALID_ORDER_DATA_TYPES = [
50 | self::DOM_TEXT,
51 | self::DOM_SELECT,
52 | self::DOM_CHECKBOX,
53 | ];
54 |
55 | /**
56 | * The column name.
57 | *
58 | * @var string
59 | */
60 | private $_name;
61 |
62 | /**
63 | * If the column is or not a database column.
64 | *
65 | * @var bool
66 | */
67 | private $_database;
68 |
69 | /**
70 | * If the column is or not a database column.
71 | *
72 | * @var array
73 | */
74 | private $_columnSchema;
75 |
76 | /**
77 | * @var string|null
78 | */
79 | private $_cellType = null;
80 |
81 | /**
82 | * @var string|null
83 | */
84 | private $_className = null;
85 |
86 | /**
87 | * @var string|null
88 | */
89 | private $_contentPadding = null;
90 |
91 | /**
92 | * @var string|array|null
93 | */
94 | private $_createdCell = null;
95 |
96 | /**
97 | * @var integer|array|null
98 | */
99 | private $_orderData = null;
100 |
101 | /**
102 | * @var string|null
103 | */
104 | private $_orderDataType = null;
105 |
106 | /**
107 | * @var array
108 | */
109 | private $_orderSequence = [];
110 |
111 | /**
112 | * @var boolean|null
113 | */
114 | private $_orderable = null;
115 |
116 | /**
117 | * @var boolean|null
118 | */
119 | private $_searchable = null;
120 |
121 | /**
122 | * @var string|null
123 | */
124 | private $_title = null;
125 |
126 | /**
127 | * @var string|null
128 | */
129 | private $_type = null;
130 |
131 | /**
132 | * @var boolean|null
133 | */
134 | private $_visible = null;
135 |
136 | /**
137 | * @var string|null
138 | */
139 | private $_width = null;
140 |
141 | /**
142 | * Column constructor.
143 | *
144 | * @param string $name
145 | * @param string|null $title
146 | * @param bool $database
147 | * @param array $columnSchema
148 | */
149 | public function __construct(string $name, string $title = null, bool $database = true, array $columnSchema = []) {
150 | $this->_name = $name;
151 | if (!empty($title)) {
152 | $this->_title = $title;
153 | } elseif ($database === true) {
154 | $this->_title = Inflector::humanize(explode('.', $name)[1]);
155 | } else {
156 | $this->_title = Inflector::humanize($name);
157 | }
158 |
159 | $this->_database = $database;
160 | $this->_columnSchema = $columnSchema;
161 | }
162 |
163 | /**
164 | * Set the attributes using a Column class.
165 | *
166 | * @param \DataTables\Table\Column $column
167 | * @return void
168 | */
169 | public function setDefault(Column $column): void {
170 | $ignoredMethods = ['setDefault', 'setTitle', 'setDatabase', 'setName'];
171 | $methods = get_class_methods($this);
172 | foreach ($methods as $method) {
173 | if (substr($method, 0, 3) === 'set' && !in_array($method, $ignoredMethods)) {
174 | $setMethod = $method;
175 | $getMethod = substr_replace($method, 'get', 0, 3);
176 | $checkMethod = substr_replace($method, 'is', 0, 3);
177 | if (in_array($getMethod, $methods)) {
178 | $this->{$setMethod}($column->{$getMethod}());
179 | } elseif (in_array($checkMethod, $methods)) {
180 | $this->{$setMethod}($column->{$checkMethod}());
181 | } else {
182 | throw new FatalErrorException("Method getter '$getMethod' or checker '$checkMethod' not found.");
183 | }
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * Get column name
190 | *
191 | * @return string
192 | */
193 | public function getName(): string {
194 | return $this->_name;
195 | }
196 |
197 | /**
198 | * Check if is a database column or not.
199 | *
200 | * @return bool
201 | */
202 | public function isDatabase(): bool {
203 | return $this->_database;
204 | }
205 |
206 | /**
207 | * Getter method.
208 | * Change the cell type created for the column - either TD cells or TH cells.
209 | * This can be useful as TH cells have semantic meaning in the table body, allowing them to act as a header for a
210 | * row (you may wish to add scope='row' to the TH elements using columns.createdCell option).
211 | *
212 | * @return string|null
213 | * @link https://datatables.net/reference/option/columns.cellType
214 | */
215 | public function getCellType(): ?string {
216 | return $this->_cellType;
217 | }
218 |
219 | /**
220 | * Setter method.
221 | * Change the cell type created for the column - either TD cells or TH cells.
222 | * This can be useful as TH cells have semantic meaning in the table body, allowing them to act as a header for a
223 | * row (you may wish to add scope='row' to the TH elements using columns.createdCell option).
224 | *
225 | * @param string|null $cellType
226 | * @return \DataTables\Table\Column
227 | * @link https://datatables.net/reference/option/columns.cellType
228 | */
229 | public function setCellType(?string $cellType): self {
230 | if (!in_array($cellType, ['td', 'th']) && !empty($cellType)) {
231 | throw new InvalidArgumentException("\$cellType must be 'td' or 'th'. Found: $cellType.");
232 | }
233 | $this->_cellType = $cellType;
234 | return $this;
235 | }
236 |
237 | /**
238 | * Getter method.
239 | * Quite simply this option adds a class to each cell in a column, regardless of if the table source is from DOM,
240 | * Javascript or Ajax. This can be useful for styling columns.
241 | *
242 | * @return string|null
243 | * @link https://datatables.net/reference/option/columns.className
244 | */
245 | public function getClassName(): ?string {
246 | return $this->_className;
247 | }
248 |
249 | /**
250 | * Setter method.
251 | * Quite simply this option adds a class to each cell in a column, regardless of if the table source is from DOM,
252 | * Javascript or Ajax. This can be useful for styling columns.
253 | *
254 | * @param string|null $className
255 | * @return \DataTables\Table\Column
256 | * @link https://datatables.net/reference/option/columns.className
257 | */
258 | public function setClassName(?string $className): self {
259 | $this->_className = $className;
260 | return $this;
261 | }
262 |
263 | /**
264 | * Getter method.
265 | * Quite simply this option adds a class to each cell in a column, regardless of if the table source is from DOM,
266 | * Javascript or Ajax. This can be useful for styling columns.
267 | *
268 | * @return string|null
269 | * @link https://datatables.net/reference/option/columns.contentPadding
270 | */
271 | public function getContentPadding(): ?string {
272 | return $this->_contentPadding;
273 | }
274 |
275 | /**
276 | * Setter method.
277 | * The first thing to say about this property is that generally you shouldn't need this!
278 | *
279 | * Having said that, it can be useful on rare occasions. When DataTables calculates the column widths to assign to
280 | * each column, it finds the longest string in each column and then constructs a temporary table and reads the
281 | * widths from that. The problem with this is that "mmm" is much wider then "iiii", but the latter is a longer
282 | * string - thus the calculation can go wrong (doing it properly and putting it into an DOM object and measuring
283 | * that is horribly slow!). Thus as a "work around" we provide this option. It will append its value to the text
284 | * that is found to be the longest string for the column - i.e. padding.
285 | *
286 | * @param string $contentPadding
287 | * @return \DataTables\Table\Column
288 | * @link https://datatables.net/reference/option/columns.contentPadding
289 | */
290 | public function setContentPadding(?string $contentPadding): self {
291 | $this->_contentPadding = $contentPadding;
292 | return $this;
293 | }
294 |
295 | /**
296 | * Getter method.
297 | * This is a callback function that is executed whenever a cell is created (Ajax source, etc) or read from a DOM
298 | * source. It can be used as a complement to columns.render allowing modification of the cell's DOM element (add
299 | * background colour for example) when the element is created (cells may not be immediately created on table
300 | * initialisation if deferRender is enabled, or if rows are dynamically added using the API (rows.add()).
301 | *
302 | * This is the counterpart callback for rows, which use the createdRow option.
303 | *
304 | * Accessible parameters inside js function:
305 | * - cell (node) - The TD node that has been created.
306 | * - cellData (any) - Cell data. If you use columns.render to modify the data, use $(cell).html() to get and modify
307 | * the rendered data. The information given here is the original and unmodified data from the data source.
308 | * - rowData (any) - Data source object / array for the whole row.
309 | * - rowIndex (integer) - DataTables' internal index for the row.
310 | * - colIndex (integer) - DataTables' internal index for the column.
311 | *
312 | * @return string|array|null
313 | * @link https://datatables.net/reference/option/columns.createdCell
314 | * @link https://datatables.net/reference/type/node
315 | * @link https://datatables.net/reference/type/integer
316 | */
317 | public function getCreatedCell() {
318 | return $this->_createdCell;
319 | }
320 |
321 | /**
322 | * Setter method.
323 | * This is a callback function that is executed whenever a cell is created (Ajax source, etc) or read from a DOM
324 | * source. It can be used as a complement to columns.render allowing modification of the cell's DOM element (add
325 | * background colour for example) when the element is created (cells may not be immediately created on table
326 | * initialisation if deferRender is enabled, or if rows are dynamically added using the API (rows.add()).
327 | *
328 | * This is the counterpart callback for rows, which use the createdRow option.
329 | *
330 | * Accessible parameters inside js function:
331 | * - cell (node) - The TD node that has been created.
332 | * - cellData (any) - Cell data. If you use columns.render to modify the data, use $(cell).html() to get and modify
333 | * the rendered data. The information given here is the original and unmodified data from the data source.
334 | * - rowData (any) - Data source object / array for the whole row.
335 | * - rowIndex (integer) - DataTables' internal index for the row.
336 | * - colIndex (integer) - DataTables' internal index for the column.
337 | *
338 | * @param string|array|null $bodyOrParams To use application template file, leave blank or pass an array with params
339 | * that will be used in file. To use the body mode, pass an string that will
340 | * putted inside the js function.
341 | * @return \DataTables\Table\Column
342 | * @link https://datatables.net/reference/option/columns.createdCell
343 | * @link https://datatables.net/reference/type/node
344 | * @link https://datatables.net/reference/type/integer
345 | */
346 | public function setCreatedCell($bodyOrParams = []): self {
347 | $bodyOrParamsType = getType($bodyOrParams);
348 | $validTypes = ['string', 'array', 'NULL'];
349 | $validTypesString = str_replace(' and ', ' or ', Text::toList($validTypes));
350 | if (!in_array($bodyOrParamsType, $validTypes)) {
351 | throw new InvalidArgumentException("In \$bodyOrParams you can use only $validTypesString. Found: '$bodyOrParamsType'.");
352 | }
353 | $this->_createdCell = $bodyOrParams;
354 | return $this;
355 | }
356 |
357 | /**
358 | * Getter method.
359 | * Allows a column's sorting to take either the data from a different (often hidden) column as the data to sort, or
360 | * data from multiple columns.
361 | *
362 | * A common example of this is a table which contains first and last name columns next to each other, it is
363 | * intuitive that they would be linked together to multi-column sort. Another example, with a single column, is the
364 | * case where the data shown to the end user is not directly sortable itself (a column with images in it), but
365 | * there is some meta data than can be sorted (e.g. file name) - note that orthogonal data is an alternative method
366 | * that can be used for this.
367 | *
368 | * @return int|array|null
369 | * @link https://datatables.net/reference/option/columns.orderData
370 | */
371 | public function getOrderData() {
372 | return $this->_orderData;
373 | }
374 |
375 | /**
376 | * Setter method.
377 | * Allows a column's sorting to take either the data from a different (often hidden) column as the data to sort, or
378 | * data from multiple columns.
379 | *
380 | * A common example of this is a table which contains first and last name columns next to each other, it is
381 | * intuitive that they would be linked together to multi-column sort. Another example, with a single column, is the
382 | * case where the data shown to the end user is not directly sortable itself (a column with images in it), but
383 | * there is some meta data than can be sorted (e.g. file name) - note that orthogonal data is an alternative method
384 | * that can be used for this.
385 | *
386 | * @param int|array|null $orderData
387 | * @return \DataTables\Table\Column
388 | * @link https://datatables.net/reference/option/columns.orderData
389 | */
390 | public function setOrderData($orderData): self {
391 | $orderDataType = getType($orderData);
392 | $validTypes = ['integer', 'array', 'NULL'];
393 | $validTypesString = str_replace(' and ', ' or ', Text::toList($validTypes));
394 | if (is_array($orderData)) {
395 | Validator::getInstance()->checkKeysValueTypesOrFail($orderData, 'integer', 'integer', '$orderData');
396 | } elseif ($orderDataType === 'integer' && $orderData < 0) {
397 | throw new InvalidArgumentException("In \$orderData must be greater or equal 0. Found: '$orderData'.");
398 | } elseif (!in_array($orderDataType, $validTypes)) {
399 | throw new InvalidArgumentException("In \$orderData you can use only $validTypesString. Found: '$orderDataType'.");
400 | }
401 | $this->_orderData = $orderData;
402 | return $this;
403 | }
404 |
405 | /**
406 | * Getter method.
407 | * DataTables' primary order method (the ordering feature) makes use of data that has been cached in memory rather
408 | * than reading the data directly from the DOM every time an order is performed for performance reasons (reading
409 | * from the DOM is inherently slow). However, there are times when you do actually want to read directly from the
410 | * DOM, acknowledging that there will be a performance hit, for example when you have form elements in the table
411 | * and the end user can alter the values. This configuration option is provided to allow plug-ins to provide this
412 | * capability in DataTables.
413 | *
414 | * Please note that there are no columns.orderDataType plug-ins built into DataTables, they must be added
415 | * separately. See the DataTables sorting plug-ins page for further information.
416 | *
417 | * @return string|null
418 | * @link https://datatables.net/reference/option/columns.orderDataType
419 | * @link https://datatables.net/plug-ins/sorting/
420 | */
421 | public function getOrderDataType(): ?string {
422 | return $this->_orderDataType;
423 | }
424 |
425 | /**
426 | * Setter method.
427 | * DataTables' primary order method (the ordering feature) makes use of data that has been cached in memory rather
428 | * than reading the data directly from the DOM every time an order is performed for performance reasons (reading
429 | * from the DOM is inherently slow). However, there are times when you do actually want to read directly from the
430 | * DOM, acknowledging that there will be a performance hit, for example when you have form elements in the table
431 | * and the end user can alter the values. This configuration option is provided to allow plug-ins to provide this
432 | * capability in DataTables.
433 | *
434 | * Please note that there are no columns.orderDataType plug-ins built into DataTables, they must be added
435 | * separately. See the DataTables sorting plug-ins page for further information.
436 | *
437 | * @param string|null $orderDataType
438 | * @return \DataTables\Table\Column
439 | * @link https://datatables.net/reference/option/columns.orderDataType
440 | * @link https://datatables.net/plug-ins/sorting/
441 | */
442 | public function setOrderDataType(?string $orderDataType): self {
443 | $validOrderDataTypeString = str_replace(' and ', ' or ', Text::toList(static::VALID_ORDER_DATA_TYPES));
444 | if (!in_array($orderDataType, static::VALID_ORDER_DATA_TYPES) && !empty($orderDataType)) {
445 | throw new InvalidArgumentException("In \$orderDataType you can use only $validOrderDataTypeString. Found: '$orderDataType'.");
446 | }
447 | $this->_orderDataType = $orderDataType;
448 | return $this;
449 | }
450 |
451 | /**
452 | * Getter method.
453 | * You can control the default ordering direction, and even alter the behaviour of the order handler (i.e. only
454 | * allow ascending sorting etc) using this parameter.
455 | *
456 | * @return array
457 | * @link https://datatables.net/reference/option/columns.orderSequence
458 | */
459 | public function getOrderSequence(): array {
460 | return $this->_orderSequence;
461 | }
462 |
463 | /**
464 | * Setter method.
465 | * You can control the default ordering direction, and even alter the behaviour of the order handler (i.e. only
466 | * allow ascending sorting etc) using this parameter.
467 | *
468 | * @param array $orderSequence
469 | * @return \DataTables\Table\Column
470 | * @link https://datatables.net/reference/option/columns.orderSequence
471 | */
472 | public function setOrderSequence(array $orderSequence = []): self {
473 | Validator::getInstance()->checkKeysValueTypesOrFail($orderSequence, 'integer', 'string', '$orderSequence');
474 | foreach ($orderSequence as $item) {
475 | if (!in_array($item, ['asc', 'desc'])) {
476 | throw new InvalidArgumentException("In \$orderDataType you can use only 'asc' or 'desc'. Found: '$item'.");
477 | }
478 | }
479 | $this->_orderSequence = $orderSequence;
480 | return $this;
481 | }
482 |
483 | /**
484 | * Checker method.
485 | * Using this parameter, you can remove the end user's ability to order upon a column. This might be useful for
486 | * generated content columns, for example if you have 'Edit' or 'Delete' buttons in the table.
487 | *
488 | * Note that this option only affects the end user's ability to order a column. Developers are still able to order
489 | * a column using the order option or the order() method if required.
490 | *
491 | * @return bool|null
492 | * @link https://datatables.net/reference/option/columns.orderable
493 | */
494 | public function isOrderable(): ?bool {
495 | return $this->_orderable;
496 | }
497 |
498 | /**
499 | * Setter method.
500 | * Using this parameter, you can remove the end user's ability to order upon a column. This might be useful for
501 | * generated content columns, for example if you have 'Edit' or 'Delete' buttons in the table.
502 | *
503 | * Note that this option only affects the end user's ability to order a column. Developers are still able to order
504 | * a column using the order option or the order() method if required.
505 | *
506 | * @param bool|null $orderable
507 | * @return \DataTables\Table\Column
508 | * @link https://datatables.net/reference/option/columns.orderable
509 | */
510 | public function setOrderable(?bool $orderable): self {
511 | $this->_orderable = $orderable;
512 | return $this;
513 | }
514 |
515 | /**
516 | * Checker method.
517 | * Using this parameter, you can define if DataTables should include this column in the filterable data in the
518 | * table. You may want to use this option to disable search on generated columns such as 'Edit' and 'Delete'
519 | * buttons for example.
520 | *
521 | * @return bool|null
522 | * @link https://datatables.net/reference/option/columns.searchable
523 | */
524 | public function isSearchable(): ?bool {
525 | return $this->_searchable;
526 | }
527 |
528 | /**
529 | * Setter method.
530 | * Using this parameter, you can define if DataTables should include this column in the filterable data in the
531 | * table. You may want to use this option to disable search on generated columns such as 'Edit' and 'Delete'
532 | * buttons for example.
533 | *
534 | * @param bool|null $searchable
535 | * @return \DataTables\Table\Column
536 | * @link https://datatables.net/reference/option/columns.searchable
537 | */
538 | public function setSearchable(?bool $searchable): self {
539 | $this->_searchable = $searchable;
540 | return $this;
541 | }
542 |
543 | /**
544 | * Getter method.
545 | * The titles of columns are typically read directly from the DOM (from the cells in the THEAD element), but it can
546 | * often be useful to either override existing values, or have DataTables actually construct a header with column
547 | * titles for you (for example if there is not a THEAD element in the table before DataTables is constructed). This
548 | * option is available to provide that ability.
549 | *
550 | * Please note that when constructing a header, DataTables can only construct a simple header with a single cell
551 | * for each column. Complex headers with colspan and rowspan attributes must either already be defined in the
552 | * document, or be constructed using standard DOM / jQuery methods.
553 | *
554 | * @return string
555 | * @link https://datatables.net/reference/option/columns.title
556 | */
557 | public function getTitle(): string {
558 | return $this->_title;
559 | }
560 |
561 | /**
562 | * Setter method.
563 | * The titles of columns are typically read directly from the DOM (from the cells in the THEAD element), but it can
564 | * often be useful to either override existing values, or have DataTables actually construct a header with column
565 | * titles for you (for example if there is not a THEAD element in the table before DataTables is constructed). This
566 | * option is available to provide that ability.
567 | *
568 | * Please note that when constructing a header, DataTables can only construct a simple header with a single cell
569 | * for each column. Complex headers with colspan and rowspan attributes must either already be defined in the
570 | * document, or be constructed using standard DOM / jQuery methods.
571 | *
572 | * @param string $title
573 | * @return \DataTables\Table\Column
574 | * @link https://datatables.net/reference/option/columns.title
575 | */
576 | public function setTitle(string $title): self {
577 | $this->_title = $title;
578 | return $this;
579 | }
580 |
581 | /**
582 | * Getter method.
583 | * When operating in client-side processing mode, DataTables can process the data used for the display in each cell
584 | * in a manner suitable for the action being performed. For example, HTML tags will be removed from the strings
585 | * used for filter matching, while sort formatting may remove currency symbols to allow currency values to be
586 | * sorted numerically. The formatting action performed to normalise the data so it can be ordered and searched
587 | * depends upon the column's type.
588 | *
589 | * DataTables has a number of built in types which are automatically detected:
590 | * - date - Date / time values. Note that DataTables' built in date parsing uses Javascript's Date.parse() method
591 | * which supports only a very limited subset of dates. Additional date format support can be added through the
592 | * use of plug-ins.
593 | * - Sorting - sorted chronologically
594 | * - Filtering - no effect
595 | * - num - Simple number sorting.
596 | * - Sorting - sorted numerically
597 | * - Filtering - no effect
598 | * - num-fmt - Numeric sorting of formatted numbers. Numbers which are formatted with thousands separators,
599 | * currency symbols or a percentage indicator will be sorted numerically automatically by DataTables.
600 | * - Supported built-in currency symbols are $, £, € and ¥.
601 | * - Supported built-in thousands separators are ' and ,.
602 | * Examples:
603 | * - $100,000 - sorted as 100000
604 | * - £10'000 - sorted as 10000
605 | * - 5'000 - sorted as 5000
606 | * - 40% - sorted as 40
607 | * - Sorting - sorted numerically
608 | * - Filtering - no effect
609 | * - html-num - As per the num option, but with HTML tags also in the data.
610 | * - Sorting - sorted numerically
611 | * - Filtering - HTML tags removed from filtering string
612 | * - html-num-fmt - As per the num-fmt option, but with HTML tags also in the data.
613 | * - Sorting - sorted numerically
614 | * - Filtering - HTML tags removed from filtering string
615 | * - html - Basic string processing for HTML tags
616 | * - Sorting - sorted with HTML tags removed
617 | * - Filtering - HTML tags removed from filtering string
618 | * - string - Fall back type if the data in the column does not match the requirements for the other data types
619 | * (above).
620 | * - Sorting - no effect
621 | * - Filtering - no effect
622 | *
623 | * It is expected that the above options will cover the majority of data types used with DataTables, however, data
624 | * is flexible and comes in many forms, so additional types with different effects can be added through the use of
625 | * plug-ins. This provides the ability to sort almost any data format imaginable!
626 | *
627 | * As an optimisation, if you know the column type in advance, you can set the value using this option, saving
628 | * DataTables from running its auto detection routine.
629 | *
630 | * Please note that if you are using server-side processing (serverSide) this option has no effect since the
631 | * ordering and search actions are performed by a server-side script.
632 | *
633 | * @return string|null
634 | * @link https://datatables.net/reference/option/columns.type
635 | */
636 | public function getType(): ?string {
637 | return $this->_type;
638 | }
639 |
640 | /**
641 | * Setter method.
642 | * When operating in client-side processing mode, DataTables can process the data used for the display in each cell
643 | * in a manner suitable for the action being performed. For example, HTML tags will be removed from the strings
644 | * used for filter matching, while sort formatting may remove currency symbols to allow currency values to be
645 | * sorted numerically. The formatting action performed to normalise the data so it can be ordered and searched
646 | * depends upon the column's type.
647 | *
648 | * DataTables has a number of built in types which are automatically detected:
649 | * - date - Date / time values. Note that DataTables' built in date parsing uses Javascript's Date.parse() method
650 | * which supports only a very limited subset of dates. Additional date format support can be added through the
651 | * use of plug-ins.
652 | * - Sorting - sorted chronologically
653 | * - Filtering - no effect
654 | * - num - Simple number sorting.
655 | * - Sorting - sorted numerically
656 | * - Filtering - no effect
657 | * - num-fmt - Numeric sorting of formatted numbers. Numbers which are formatted with thousands separators,
658 | * currency symbols or a percentage indicator will be sorted numerically automatically by DataTables.
659 | * - Supported built-in currency symbols are $, £, € and ¥.
660 | * - Supported built-in thousands separators are ' and ,.
661 | * Examples:
662 | * - $100,000 - sorted as 100000
663 | * - £10'000 - sorted as 10000
664 | * - 5'000 - sorted as 5000
665 | * - 40% - sorted as 40
666 | * - Sorting - sorted numerically
667 | * - Filtering - no effect
668 | * - html-num - As per the num option, but with HTML tags also in the data.
669 | * - Sorting - sorted numerically
670 | * - Filtering - HTML tags removed from filtering string
671 | * - html-num-fmt - As per the num-fmt option, but with HTML tags also in the data.
672 | * - Sorting - sorted numerically
673 | * - Filtering - HTML tags removed from filtering string
674 | * - html - Basic string processing for HTML tags
675 | * - Sorting - sorted with HTML tags removed
676 | * - Filtering - HTML tags removed from filtering string
677 | * - string - Fall back type if the data in the column does not match the requirements for the other data types
678 | * (above).
679 | * - Sorting - no effect
680 | * - Filtering - no effect
681 | *
682 | * It is expected that the above options will cover the majority of data types used with DataTables, however, data
683 | * is flexible and comes in many forms, so additional types with different effects can be added through the use of
684 | * plug-ins. This provides the ability to sort almost any data format imaginable!
685 | *
686 | * As an optimisation, if you know the column type in advance, you can set the value using this option, saving
687 | * DataTables from running its auto detection routine.
688 | *
689 | * Please note that if you are using server-side processing (serverSide) this option has no effect since the
690 | * ordering and search actions are performed by a server-side script.
691 | *
692 | * @param string|null $type
693 | * @return \DataTables\Table\Column
694 | * @link https://datatables.net/reference/option/columns.type
695 | */
696 | public function setType(?string $type): self {
697 | $validTypesString = str_replace(' and ', ' or ', Text::toList(static::VALID_TYPES));
698 | if (!in_array($type, static::VALID_TYPES) && !empty($type)) {
699 | throw new InvalidArgumentException("Type must be $validTypesString. Found: '$type'.");
700 | }
701 | $this->_type = $type;
702 | return $this;
703 | }
704 |
705 | /**
706 | * Checker method.
707 | * DataTables and show and hide columns dynamically through use of this option and the column().visible() /
708 | * columns().visible() methods. This option can be used to get the initial visibility state of the column, with the
709 | * API methods used to alter that state at a later time.
710 | *
711 | * This can be particularly useful if your table holds a large number of columns and you wish the user to have the
712 | * ability to control which columns they can see, or you have data in the table that the end user shouldn't see
713 | * (for example a database ID column).
714 | *
715 | * @return bool|null
716 | * @link https://datatables.net/reference/option/columns.visible
717 | */
718 | public function isVisible(): ?bool {
719 | return $this->_visible;
720 | }
721 |
722 | /**
723 | * Setter method.
724 | * DataTables and show and hide columns dynamically through use of this option and the column().visible() /
725 | * columns().visible() methods. This option can be used to get the initial visibility state of the column, with the
726 | * API methods used to alter that state at a later time.
727 | *
728 | * This can be particularly useful if your table holds a large number of columns and you wish the user to have the
729 | * ability to control which columns they can see, or you have data in the table that the end user shouldn't see
730 | * (for example a database ID column).
731 | *
732 | * @param bool|null $visible
733 | * @return \DataTables\Table\Column
734 | * @link https://datatables.net/reference/option/columns.visible
735 | */
736 | public function setVisible(?bool $visible): self {
737 | $this->_visible = $visible;
738 | return $this;
739 | }
740 |
741 | /**
742 | * Getter method.
743 | * This parameter can be used to define the width of a column, and may take any CSS value (3em, 20px etc).
744 | *
745 | * Please note that pixel perfect column width is virtually impossible to achieve in tables with dynamic content,
746 | * so do not be surprised if the width of the column if off by a few pixels from what you assign using this
747 | * property. Column width in tables depends upon many properties such as cell borders, table borders, the
748 | * border-collapse property, the content of the table and many other properties. Both DataTables and the browsers
749 | * attempt to lay the table out in an optimal manner taking this options all into account.
750 | *
751 | * @return string|null
752 | * @link https://datatables.net/reference/option/columns.width
753 | */
754 | public function getWidth(): ?string {
755 | return $this->_width;
756 | }
757 |
758 | /**
759 | * Setter method.
760 | * This parameter can be used to define the width of a column, and may take any CSS value (3em, 20px etc).
761 | *
762 | * Please note that pixel perfect column width is virtually impossible to achieve in tables with dynamic content,
763 | * so do not be surprised if the width of the column if off by a few pixels from what you assign using this
764 | * property. Column width in tables depends upon many properties such as cell borders, table borders, the
765 | * border-collapse property, the content of the table and many other properties. Both DataTables and the browsers
766 | * attempt to lay the table out in an optimal manner taking this options all into account.
767 | *
768 | * @param string|null $width
769 | * @return \DataTables\Table\Column
770 | * @link https://datatables.net/reference/option/columns.width
771 | */
772 | public function setWidth(?string $width): self {
773 | $this->_width = $width;
774 | return $this;
775 | }
776 |
777 | }
778 |
--------------------------------------------------------------------------------
/src/Table/Columns.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table;
13 |
14 | use Cake\Error\FatalErrorException;
15 | use Cake\Utility\Inflector;
16 | use InvalidArgumentException;
17 |
18 | /**
19 | * Class Columns
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | final class Columns {
26 |
27 | /**
28 | * Created columns.
29 | *
30 | * @var \DataTables\Table\Column[]
31 | */
32 | private $_columns = [];
33 |
34 | /**
35 | * A selected Tables class.
36 | *
37 | * @var \DataTables\Table\Tables
38 | */
39 | private $_tables;
40 |
41 | /**
42 | * Default application column configuration.
43 | *
44 | * @var \DataTables\Table\Column
45 | */
46 | public $Default;
47 |
48 | /**
49 | * Columns constructor.
50 | *
51 | * @param \DataTables\Table\Tables $tables
52 | */
53 | public function __construct(Tables $tables) {
54 | $this->_tables = $tables;
55 | $this->Default = new Column('default', 'empty', false);
56 | }
57 |
58 | /**
59 | * Return all configured columns.
60 | *
61 | * @return array
62 | */
63 | public function getColumns(): array {
64 | return $this->_columns;
65 | }
66 |
67 | /**
68 | * Add a database column to DataTables table.
69 | *
70 | * @param string $dataBaseField
71 | * @param string|null $title
72 | * @return \DataTables\Table\Column
73 | */
74 | public function addDatabaseColumn(string $dataBaseField, ?string $title = null): Column {
75 | $column = $this->normalizeDataTableField($dataBaseField, $title);
76 | return $this->saveColumn($column);
77 | }
78 |
79 | /**
80 | * Add a non database column to DataTables table.
81 | *
82 | * @param string $label
83 | * @param string|null $title
84 | * @return \DataTables\Table\Column
85 | */
86 | public function addNonDatabaseColumn(string $label, ?string $title = null): Column {
87 | $column = new Column($label, $title, false);
88 | return $this->saveColumn($column);
89 | }
90 |
91 | /**
92 | * Save the column on array.
93 | *
94 | * @param \DataTables\Table\Column $column
95 | * @return \DataTables\Table\Column
96 | */
97 | private function saveColumn(Column $column): Column {
98 | foreach ($this->_columns as $key => $savedColumn) {
99 | if ($savedColumn->getName() === $column->getName()) {
100 | throw new FatalErrorException("Column '{$column->getName()}' already exist in index $key.");
101 | }
102 | }
103 | $column->setDefault($this->Default);
104 | $this->_columns[] = $column;
105 | return $column;
106 | }
107 |
108 | /**
109 | * Check if class, tables, fields and associations exists, and after normalize the name.
110 | *
111 | * @param string $dataBaseField
112 | * @param string|null $title
113 | * @return \DataTables\Table\Column
114 | */
115 | private function normalizeDataTableField(string $dataBaseField, ?string $title): Column {
116 | $ormTable = $this->_tables->getOrmTable();
117 | $explodedDataBaseField = explode('.', $dataBaseField);
118 | if (count($explodedDataBaseField) === 2) {
119 | $table = Inflector::camelize($explodedDataBaseField[0]);
120 | $column = Inflector::dasherize($explodedDataBaseField[1]);
121 | } elseif (count($explodedDataBaseField) == 1) {
122 | $table = Inflector::camelize($ormTable->getAlias());
123 | $column = Inflector::dasherize($explodedDataBaseField[0]);
124 | } else {
125 | throw new InvalidArgumentException("$dataBaseField is a invalid \$dataBaseField.");
126 | }
127 |
128 | if ($table === Inflector::camelize($ormTable->getAlias())) {
129 | if (!$ormTable->getSchema()->hasColumn($column)) {
130 | throw new InvalidArgumentException("The field '$column' not exists in '$table'");
131 | }
132 | $columnSchema = $this->_tables->getOrmTable()->getSchema()->getColumn($column);
133 | } else {
134 | if (!$ormTable->hasAssociation($table)) {
135 | throw new InvalidArgumentException("The table '$table' isn't associated with '" . $ormTable->getAlias() . "'.");
136 | }
137 | /** @var \Cake\ORM\Association|\Cake\ORM\Table $association */
138 | $association = $ormTable->getAssociation($table);
139 | if (!$association->getSchema()->hasColumn($column)) {
140 | throw new InvalidArgumentException("The field '$column' not exists in '{$association->getAlias()}'");
141 | }
142 | $columnSchema = $association->getSchema()->getColumn($column);
143 | }
144 | $column = new Column("$table.$column", $title, true, $columnSchema);
145 |
146 | return $column;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/src/Table/ConfigBundle.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table;
13 |
14 | use Cake\View\View;
15 | use DataTables\Table\Option\MainOption;
16 |
17 | /**
18 | * Class ConfigBundle
19 | *
20 | * @author Allan Carvalho
21 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
22 | * @link https://github.com/allanmcarvalho/cakephp-datatables
23 | */
24 | final class ConfigBundle {
25 |
26 | /**
27 | * @var string The md5 used to check changes.
28 | */
29 | private $_checkMd5;
30 |
31 | /**
32 | * @var \DataTables\Table\QueryBaseState The DataTables query state.
33 | */
34 | public $Query;
35 |
36 | /**
37 | * @var \DataTables\Table\Columns The DataTables table columns.
38 | */
39 | public $Columns;
40 |
41 | /**
42 | * @var \DataTables\Table\Option\MainOption The DataTables JS Options.
43 | */
44 | public $Options;
45 |
46 | /**
47 | * ConfigBundle constructor.
48 | *
49 | * @param string $checkMd5 The md5 used to check changes.
50 | * @param \DataTables\Table\QueryBaseState $queryBaseState The DataTables base query.
51 | * @param \DataTables\Table\Columns $_columns The DataTables table columns.
52 | * @param \DataTables\Table\Option\MainOption $options The DataTables JS Options.
53 | */
54 | public function __construct(string $checkMd5, QueryBaseState $queryBaseState, Columns $_columns, MainOption $options) {
55 | $this->_checkMd5 = $checkMd5;
56 | $this->Query = $queryBaseState;
57 | $this->Columns = $_columns;
58 | $this->Options = $options;
59 | }
60 |
61 | /**
62 | * @return string
63 | */
64 | public function getCheckMd5(): string {
65 | return $this->_checkMd5;
66 | }
67 |
68 | /**
69 | * @return string
70 | */
71 | public function getUniqueId(): string {
72 | return $this->_checkMd5;
73 | }
74 |
75 | /**
76 | * @param \Cake\View\View $view
77 | * @return string
78 | */
79 | public function generateTableHtml(View $view): string {
80 | return $view->cell('DataTables.DataTables::table', [$this->Columns])->render();
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/Table/Option/CallBack/MainCallBack.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table\Option\CallBack;
13 |
14 | use Cake\Core\Configure;
15 | use Cake\Error\FatalErrorException;
16 | use Cake\Utility\Inflector;
17 | use DataTables\Tools\Validator;
18 | use InvalidArgumentException;
19 | use Twig\Environment;
20 | use Twig\Loader\FilesystemLoader;
21 |
22 | /**
23 | * Class MainCallBack
24 | *
25 | * @author Allan Carvalho
26 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
27 | * @link https://github.com/allanmcarvalho/cakephp-datatables
28 | */
29 | final class MainCallBack {
30 |
31 | /**
32 | * @var string
33 | */
34 | protected $_callbackNamePrefix = 'callback_';
35 |
36 | /**
37 | * @var string
38 | */
39 | protected $_callbackName;
40 |
41 | /**
42 | * @var string
43 | */
44 | protected $_appTemplateFolder;
45 |
46 | /**
47 | * @var string
48 | */
49 | protected $_pluginTemplateFolder;
50 |
51 | /**
52 | * @var string
53 | */
54 | protected $_ext = '.twig';
55 |
56 | /**
57 | * @var \Twig\Environment
58 | */
59 | protected $_twig;
60 |
61 | /**
62 | * @var \Twig\Loader\FilesystemLoader
63 | */
64 | protected $_twigLoader;
65 |
66 | /**
67 | * Storage a instance of object.
68 | *
69 | * @var self[]
70 | */
71 | public static $instance;
72 |
73 | /**
74 | * MainCallBack constructor.
75 | *
76 | * @param string $callbackName
77 | * @param string $tablesName
78 | * @param string $config
79 | */
80 | public function __construct(string $callbackName, string $tablesName, string $config) {
81 | $basePath = Configure::read('DataTables.resources.templates');
82 | if (substr($basePath, -1, 1) !== DS) {
83 | $basePath .= DS;
84 | }
85 | $this->_callbackName = $this->_callbackNamePrefix . $callbackName . $this->_ext;
86 | $this->_appTemplateFolder = $basePath . $tablesName . DS . $config . DS;
87 | $this->_pluginTemplateFolder = DATA_TABLES_TEMPLATES . 'twig' . DS . 'js' . DS . 'functions' . DS;
88 | $this->_twigLoader = new FilesystemLoader();
89 | $this->_twig = new Environment($this->_twigLoader);
90 | if (Configure::read('debug') === true) {
91 | $this->_twig->setCache(false);
92 | } else {
93 | $this->_twig->setCache(Configure::read('DataTables.resources.twigCacheFolder'));
94 | }
95 | }
96 |
97 | /**
98 | * Return a instance of builder object.
99 | *
100 | * @param string $callBack
101 | * @param string $tablesName
102 | * @param string $config
103 | * @return \DataTables\Table\Option\CallBack\MainCallBack
104 | */
105 | public static function getInstance(string $callBack, string $tablesName, string $config): MainCallBack {
106 | $callBack = Inflector::underscore($callBack);
107 | $tablesName = Inflector::camelize($tablesName);
108 | $config = Inflector::underscore($config);
109 | $md5 = md5($callBack . $tablesName . $config);
110 | if (empty(static::$instance[$md5])) {
111 | static::$instance[$md5] = new self($callBack, $tablesName, $config);
112 | }
113 | return static::$instance[$md5];
114 | }
115 |
116 | /**
117 | * Destroy all instances if exist.
118 | *
119 | * @return void
120 | */
121 | public static function destroyAllInstances(): void {
122 | static::$instance = [];
123 | }
124 |
125 | /**
126 | * Render callback js functions with application template file or body.
127 | *
128 | * @param string|array $bodyOrParams To use application template file, leave blank or pass an array with params
129 | * that will be used in file. To use the body mode, pass an string that will
130 | * putted inside the js function.
131 | * @return string
132 | * @throws \Twig\Error\LoaderError
133 | * @throws \Twig\Error\RuntimeError
134 | * @throws \Twig\Error\SyntaxError
135 | * @link https://twig.symfony.com/doc/3.x/api.html
136 | */
137 | public function render($bodyOrParams = []) {
138 | $bodyParamsType = getType($bodyOrParams);
139 | if ($bodyParamsType === 'array') {
140 | $this->checkIfFileExistsOfFail($this->_appTemplateFolder . $this->_callbackName);
141 | Validator::getInstance()->checkKeysValueTypesOrFail($bodyOrParams, 'string', '*');
142 | $this->_twigLoader->setPaths($this->_appTemplateFolder);
143 | $body = $this->_twig->render($this->_callbackName, $bodyOrParams);
144 | } elseif ($bodyParamsType === 'string') {
145 | $body = $bodyOrParams;
146 | } else {
147 | throw new InvalidArgumentException("$bodyOrParams must be 'string' or 'array'. Found: $bodyParamsType.");
148 | }
149 | $this->checkIfFileExistsOfFail($this->_pluginTemplateFolder . $this->_callbackName);
150 | $this->_twigLoader->setPaths($this->_pluginTemplateFolder);
151 | return $this->_twig->render($this->_callbackName, compact('body'));
152 | }
153 |
154 | /**
155 | * Check if a file exists or fail.
156 | *
157 | * @param string $file
158 | * @return void
159 | */
160 | private function checkIfFileExistsOfFail(string $file): void {
161 | if (!is_file($file)) {
162 | throw new FatalErrorException("File '$file' not found.");
163 | }
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/Table/Option/ChildOptionAbstract.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | namespace DataTables\Table\Option;
12 |
13 | abstract class ChildOptionAbstract extends OptionAbstract {
14 |
15 | /**
16 | * @var \DataTables\Table\Option\MainOption|null
17 | */
18 | protected $_mainOption = null;
19 |
20 | /**
21 | * ChildOptionAbstract constructor.
22 | *
23 | * @param \DataTables\Table\Option\MainOption $mainOption
24 | */
25 | public function __construct(MainOption $mainOption) {
26 | parent::__construct();
27 | $this->_mainOption = $mainOption;
28 | foreach ($this->_config as $key => $config) {
29 | $this->_setConfig($key, $config, false);
30 | }
31 | foreach ($this->_mustPrint as $key => $mustPrint) {
32 | $this->getMainOption()->setMustPrint($key, $mustPrint);
33 | }
34 | }
35 |
36 | /**
37 | * Return the MainOption class.
38 | *
39 | * @return \DataTables\Table\Option\MainOption;
40 | */
41 | protected function getMainOption(): MainOption {
42 | return $this->_mainOption;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/Table/Option/MainOption.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table\Option;
13 |
14 | use Cake\Core\Configure;
15 | use Cake\Utility\Hash;
16 | use DataTables\Table\Option\Section\ColumnsOption;
17 | use DataTables\Table\Option\Section\FeaturesOption;
18 | use DataTables\Table\Option\Section\OptionsOption;
19 |
20 | /**
21 | * Class Options
22 | *
23 | * @property \DataTables\Table\Option\Section\FeaturesOption $Features
24 | * @property \DataTables\Table\Option\Section\OptionsOption $Options
25 | * @property \DataTables\Table\Option\Section\ColumnsOption $Columns
26 | * @author Allan Carvalho
27 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
28 | * @link https://github.com/allanmcarvalho/cakephp-datatables
29 | */
30 | final class MainOption extends OptionAbstract {
31 |
32 | /**
33 | * @var array
34 | * @inheritDoc
35 | */
36 | protected $_mustPrint = [];
37 |
38 | /**
39 | * @var array
40 | * @inheritDoc
41 | */
42 | protected $_config = [];
43 |
44 | /**
45 | * Define if all options will be printed or not.
46 | *
47 | * @var bool
48 | */
49 | protected $_printAllOptions = false;
50 |
51 | /**
52 | * @inheritDoc
53 | */
54 | public function __construct() {
55 | parent::__construct();
56 | $this->Features = new FeaturesOption($this);
57 | $this->Options = new OptionsOption($this);
58 | $this->Columns = new ColumnsOption($this);
59 | }
60 |
61 | /**
62 | * Get if all options will be printed or not.
63 | *
64 | * @return bool
65 | */
66 | public function isPrintAllOptions(): bool {
67 | return $this->_printAllOptions;
68 | }
69 |
70 | /**
71 | * Tell if a field or a many fields will be printed or not.
72 | *
73 | * @param string|null $field The field that you intent to see or null for all.
74 | * @return string|array|null A value if exists or null.
75 | */
76 | public function getMustPrint(?string $field = null) {
77 | if (!empty($field)) {
78 | return Hash::get($this->_mustPrint, $field, null);
79 | }
80 |
81 | return $this->_mustPrint;
82 | }
83 |
84 | /**
85 | * Set if a field must be printed or not.
86 | *
87 | * @param string $field The field that will be changed.
88 | * @param bool $must True or false to set if it will printed or not.
89 | * @return \DataTables\Table\Option\MainOption
90 | */
91 | public function setMustPrint(string $field, bool $must = true): MainOption {
92 | $this->_mustPrint = Hash::insert($this->_mustPrint, $field, $must);
93 | return $this;
94 | }
95 |
96 | /**
97 | * Define if all options will be printed or not.
98 | *
99 | * @param bool $printAllOptions
100 | * @return $this
101 | */
102 | public function setPrintAllOptions(bool $printAllOptions): self {
103 | $this->_printAllOptions = $printAllOptions;
104 |
105 | return $this;
106 | }
107 |
108 | /**
109 | * Get a config.
110 | *
111 | * @param string|null $field The field that you intent to see or null for all.
112 | * @param string|array|null $default A default value for called config.
113 | * @return mixed A value if exists or null.
114 | */
115 | public function getConfig(?string $field = null, $default = null) {
116 | return $this->_getConfig($field, $default);
117 | }
118 |
119 | /**
120 | * Set manually a config.
121 | *
122 | * @param string $field The field that will be changed.
123 | * @param mixed $value A value intended to save at config.
124 | * @param bool $mustPrint Set or not the field as 'mustPrint'.
125 | * @return $this
126 | */
127 | public function setConfig(string $field, $value, bool $mustPrint = true): self {
128 | $this->_setConfig($field, $value, $mustPrint);
129 |
130 | return $this;
131 | }
132 |
133 | /**
134 | * Get the config as json.
135 | *
136 | * @param bool|null $printAllOptions
137 | * @return string
138 | */
139 | public function getConfigAsJson(?bool $printAllOptions = null): string {
140 | $options = 0;
141 | if (Configure::read('debug') === true) {
142 | $options = JSON_PRETTY_PRINT;
143 | }
144 | return json_encode($this->getConfigAsArray($printAllOptions), $options);
145 | }
146 |
147 | /**
148 | * Get the config as array.
149 | *
150 | * @param bool|null $printAllOptions
151 | * @return array
152 | */
153 | public function getConfigAsArray(?bool $printAllOptions = null): array {
154 | if ($printAllOptions === true || (empty($printAllOptions) && $this->_printAllOptions === true)) {
155 | return $this->_config;
156 | }
157 | $result = [];
158 | foreach (Hash::flatten($this->_mustPrint) as $key => $config) {
159 | $result = Hash::insert($result, $key, Hash::get($this->_config, $key, null));
160 | }
161 | return $result;
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/src/Table/Option/OptionAbstract.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table\Option;
13 |
14 | use Cake\Utility\Hash;
15 |
16 | /**
17 | * Class OptionAbstract
18 | *
19 | * @author Allan Carvalho
20 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
21 | * @link https://github.com/allanmcarvalho/cakephp-datatables
22 | */
23 | abstract class OptionAbstract {
24 |
25 | /**
26 | * The options that was set and/or must be printed.
27 | *
28 | * @var array
29 | */
30 | protected $_mustPrint = [];
31 |
32 | /**
33 | * DataTables Js configs
34 | *
35 | * @var array
36 | */
37 | protected $_config = [];
38 |
39 | /**
40 | * OptionAbstract constructor.
41 | */
42 | public function __construct() {
43 | }
44 |
45 | /**
46 | * Get a config.
47 | *
48 | * @param string|null $field The field that you intent to see or null for all.
49 | * @param string|array|null $default A default value for called config.
50 | * @return mixed|void A value if exists or null.
51 | */
52 | protected function _getConfig(?string $field = null, $default = null) {
53 | if ($this instanceof ChildOptionAbstract) {
54 | return $this->getMainOption()->getConfig($field, $default);
55 | }
56 | if (!empty($field)) {
57 | return Hash::get($this->_config, $field, $default);
58 | }
59 | return $this->_config;
60 | }
61 |
62 | /**
63 | * Set manually a config.
64 | *
65 | * @param string $field The field that will be changed.
66 | * @param mixed $value A value intended to save at config.
67 | * @param bool $mustPrint Set or not the field as 'mustPrint'.
68 | * @return void
69 | */
70 | protected function _setConfig(string $field, $value, bool $mustPrint = true): void {
71 | if ($this instanceof MainOption) {
72 | $this->_config = Hash::insert($this->_config, $field, $value);
73 | if ($mustPrint === true) {
74 | $this->setMustPrint($field, true);
75 | }
76 | } elseif ($this instanceof ChildOptionAbstract) {
77 | $this->getMainOption()->setConfig($field, $value, $mustPrint);
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/Table/Option/Section/ColumnsOption.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table\Option\Section;
13 |
14 | use DataTables\Table\Columns;
15 | use DataTables\Table\Option\ChildOptionAbstract;
16 | use DataTables\Table\Option\MainOption;
17 |
18 | /**
19 | * Class ColumnsOption
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | final class ColumnsOption extends ChildOptionAbstract {
26 |
27 | /**
28 | * @var array
29 | * @inheritDoc
30 | */
31 | protected $_mustPrint = [];
32 |
33 | /**
34 | * @var array
35 | * @inheritDoc
36 | */
37 | protected $_config = [
38 | 'columnDefs' => [],
39 | 'columns' => [],
40 | ];
41 |
42 | /**
43 | * Setter method.
44 | * Set all columns and defColumns options using a Columns class.
45 | *
46 | * @param \DataTables\Table\Columns $columns
47 | * @return \DataTables\Table\Option\MainOption
48 | * @link https://datatables.net/reference/option/
49 | */
50 | public function setColumns(Columns $columns): MainOption {
51 | return $this->getMainOption();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/Table/Option/Section/FeaturesOption.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table\Option\Section;
13 |
14 | use Cake\Error\FatalErrorException;
15 | use DataTables\Table\Option\ChildOptionAbstract;
16 | use DataTables\Table\Option\MainOption;
17 |
18 | /**
19 | * Class FeaturesOption
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | final class FeaturesOption extends ChildOptionAbstract {
26 |
27 | /**
28 | * @var array
29 | * @inheritDoc
30 | */
31 | protected $_mustPrint = [
32 | 'serverSide' => true,
33 | ];
34 |
35 | /**
36 | * @var array
37 | * @inheritDoc
38 | */
39 | protected $_config = [
40 | 'autoWidth' => true,
41 | 'deferRender' => false,
42 | 'info' => true,
43 | 'lengthChange' => true,
44 | 'ordering' => true,
45 | 'paging' => true,
46 | 'processing' => false,
47 | 'scrollX' => false,
48 | 'scrollY' => null,
49 | 'searching' => true,
50 | 'serverSide' => true,
51 | 'stateSave' => false,
52 | ];
53 |
54 | /**
55 | * Checker method.
56 | * Enable or disable automatic column width calculation. This can be disabled as an optimisation(it takes a finite
57 | * amount of time to calculate the widths) if the tables widths are passed in using.
58 | *
59 | * @link https://datatables.net/reference/option/autoWidth
60 | * @return bool
61 | */
62 | public function isAutoWidth(): bool {
63 | return (bool)$this->_getConfig('autoWidth');
64 | }
65 |
66 | /**
67 | * Setter method.
68 | * Enable or disable automatic column width calculation. This can be disabled as an optimisation(it takes a finite
69 | * amount of time to calculate the widths) if the tables widths are passed in using.
70 | *
71 | * @param bool $autoWidth
72 | * @return \DataTables\Table\Option\MainOption
73 | * @link https://datatables.net/reference/option/autoWidth
74 | */
75 | public function setAutoWidth(bool $autoWidth): MainOption {
76 | $this->_setConfig('autoWidth', $autoWidth);
77 |
78 | return $this->getMainOption();
79 | }
80 |
81 | /**
82 | * Checker method.
83 | * By default, when DataTables loads data from an Ajax or Javascript data source (ajax and data respectively) it
84 | * will create all HTML elements needed up-front. When working with large data sets, this operation can take a
85 | * not-insignificant amount of time, particularly in older browsers such as IE6-8. This option allows DataTables to
86 | * create the nodes (rows and cells in the table body) only when they are needed for a draw.
87 | *
88 | * As an example to help illustrate this, if you load a data set with 10,000 rows, but a paging display length of
89 | * only 10 records, rather than create all 10,000 rows, when deferred rendering is enabled, DataTables will create
90 | * only 10. When the end user then sorts, pages or filters the data the rows needed for the next display will be
91 | * created automatically. This effectively spreads the load of creating the rows across the life time of the page.
92 | *
93 | * Note that when enabled, it goes without saying that not all nodes will always be available in the table, so when
94 | * working with API methods such as columns().nodes() you must take this into account. Below shows an example of
95 | * how to use jQuery delegated events to handle such a situation.
96 | *
97 | * @link https://datatables.net/reference/option/deferRender
98 | * @return bool
99 | */
100 | public function isDeferRender(): bool {
101 | return (bool)$this->_getConfig('deferRender');
102 | }
103 |
104 | /**
105 | * Setter method.
106 | * By default, when DataTables loads data from an Ajax or Javascript data source (ajax and data respectively) it
107 | * will create all HTML elements needed up-front. When working with large data sets, this operation can take a
108 | * not-insignificant amount of time, particularly in older browsers such as IE6-8. This option allows DataTables to
109 | * create the nodes (rows and cells in the table body) only when they are needed for a draw.
110 | *
111 | * As an example to help illustrate this, if you load a data set with 10,000 rows, but a paging display length of
112 | * only 10 records, rather than create all 10,000 rows, when deferred rendering is enabled, DataTables will create
113 | * only 10. When the end user then sorts, pages or filters the data the rows needed for the next display will be
114 | * created automatically. This effectively spreads the load of creating the rows across the life time of the page.
115 | *
116 | * Note that when enabled, it goes without saying that not all nodes will always be available in the table, so when
117 | * working with API methods such as columns().nodes() you must take this into account. Below shows an example of
118 | * how to use jQuery delegated events to handle such a situation.
119 | *
120 | * @param bool $deferRender
121 | * @return \DataTables\Table\Option\MainOption
122 | * @link https://datatables.net/reference/option/deferRender
123 | */
124 | public function setDeferRender(bool $deferRender): MainOption {
125 | $this->_setConfig('deferRender', $deferRender);
126 |
127 | return $this->getMainOption();
128 | }
129 |
130 | /**
131 | * Checker method.
132 | * When this option is enabled, Datatables will show information about the table including information about
133 | * filtered data if that action is being performed. This option allows that feature to be enabled or disabled.
134 | *
135 | * Note that by default the information display is shown below the table on the left, but this can be controlled
136 | * using dom and CSS).
137 | *
138 | * @link https://datatables.net/reference/option/info
139 | * @return bool
140 | */
141 | public function isInfo(): bool {
142 | return (bool)$this->_getConfig('info');
143 | }
144 |
145 | /**
146 | * Setter method.
147 | * When this option is enabled, Datatables will show information about the table including information about
148 | * filtered data if that action is being performed. This option allows that feature to be enabled or disabled.
149 | *
150 | * Note that by default the information display is shown below the table on the left, but this can be controlled
151 | * using dom and CSS).
152 | *
153 | * @param bool $info
154 | * @return \DataTables\Table\Option\MainOption
155 | * @link https://datatables.net/reference/option/info
156 | */
157 | public function setInfo(bool $info): MainOption {
158 | $this->_setConfig('info', $info);
159 |
160 | return $this->getMainOption();
161 | }
162 |
163 | /**
164 | * Checker method.
165 | * When pagination is enabled, this option will control the display of an option for the end user to change the
166 | * number of records to be shown per page. The options shown in the list are controlled by the lengthMenu
167 | * configuration option.
168 | *
169 | * Note that by default the control is shown at the top left of the table. That can be controlled using dom and
170 | * CSS.
171 | *
172 | * If this option is disabled (false) the length change input control is removed - although the page.len() method
173 | * can still be used if you wish to programmatically change the page size and pageLength can be used to specify the
174 | * initial page length. Paging itself is not affected.
175 | *
176 | * Additionally, if pagination is disabled using the paging option, this option is automatically disabled since it
177 | * has no relevance when there is no pagination.
178 | *
179 | * @link https://datatables.net/reference/option/lengthChange
180 | * @return bool
181 | */
182 | public function isLengthChange(): bool {
183 | return (bool)$this->_getConfig('lengthChange');
184 | }
185 |
186 | /**
187 | * Setter method.
188 | * When pagination is enabled, this option will control the display of an option for the end user to change the
189 | * number of records to be shown per page. The options shown in the list are controlled by the lengthMenu
190 | * configuration option.
191 | *
192 | * Note that by default the control is shown at the top left of the table. That can be controlled using dom and
193 | * CSS.
194 | *
195 | * If this option is disabled (false) the length change input control is removed - although the page.len() method
196 | * can still be used if you wish to programmatically change the page size and pageLength can be used to specify the
197 | * initial page length. Paging itself is not affected.
198 | *
199 | * Additionally, if pagination is disabled using the paging option, this option is automatically disabled since it
200 | * has no relevance when there is no pagination.
201 | *
202 | * @param bool $lengthChange
203 | * @return \DataTables\Table\Option\MainOption
204 | * @link https://datatables.net/reference/option/lengthChange
205 | */
206 | public function setLengthChange(bool $lengthChange): MainOption {
207 | $this->_setConfig('lengthChange', $lengthChange);
208 |
209 | return $this->getMainOption();
210 | }
211 |
212 | /**
213 | * Checker method.
214 | * Enable or disable ordering of columns - it is as simple as that! DataTables, by default, allows end users to
215 | * click on the header cell for each column, ordering the table by the data in that column. The ability to order
216 | * data can be disabled using this option.
217 | *
218 | * Note that the ability to add or remove sorting of individual columns can be disabled by the columns.orderable
219 | * option for each column. This parameter is a global option - when disabled, there are no sorting actions applied
220 | * by DataTables at all.
221 | *
222 | * @link https://datatables.net/reference/option/ordering
223 | * @return bool
224 | */
225 | public function isOrdering(): bool {
226 | return (bool)$this->_getConfig('ordering');
227 | }
228 |
229 | /**
230 | * Setter method.
231 | * Enable or disable ordering of columns - it is as simple as that! DataTables, by default, allows end users to
232 | * click on the header cell for each column, ordering the table by the data in that column. The ability to order
233 | * data can be disabled using this option.
234 | *
235 | * Note that the ability to add or remove sorting of individual columns can be disabled by the columns.orderable
236 | * option for each column. This parameter is a global option - when disabled, there are no sorting actions applied
237 | * by DataTables at all.
238 | *
239 | * @param bool $ordering
240 | * @return \DataTables\Table\Option\MainOption
241 | * @link https://datatables.net/reference/option/ordering
242 | */
243 | public function setOrdering(bool $ordering): MainOption {
244 | $this->_setConfig('ordering', $ordering);
245 |
246 | return $this->getMainOption();
247 | }
248 |
249 | /**
250 | * Checker method.
251 | * DataTables can split the rows in tables into individual pages, which is an efficient method of showing a large
252 | * number of records in a small space. The end user is provided with controls to request the display of different
253 | * data as the navigate through the data. This feature is enabled by default, but if you wish to disable it, you
254 | * may do so with this parameter.
255 | *
256 | * @link https://datatables.net/reference/option/paging
257 | * @return bool
258 | */
259 | public function isPaging(): bool {
260 | return (bool)$this->_getConfig('paging');
261 | }
262 |
263 | /**
264 | * Setter method.
265 | * DataTables can split the rows in tables into individual pages, which is an efficient method of showing a large
266 | * number of records in a small space. The end user is provided with controls to request the display of different
267 | * data as the navigate through the data. This feature is enabled by default, but if you wish to disable it, you
268 | * may do so with this parameter.
269 | *
270 | * @param bool $paging
271 | * @return \DataTables\Table\Option\MainOption
272 | * @link https://datatables.net/reference/option/paging
273 | */
274 | public function setPaging(bool $paging): MainOption {
275 | $this->_setConfig('paging', $paging);
276 |
277 | return $this->getMainOption();
278 | }
279 |
280 | /**
281 | * Checker method.
282 | * Enable or disable the display of a 'processing' indicator when the table is being processed (e.g. a sort). This
283 | * is particularly useful for tables with large amounts of data where it can take a noticeable amount of time to
284 | * sort the entries.
285 | *
286 | * @link https://datatables.net/reference/option/processing
287 | * @return bool
288 | */
289 | public function isProcessing(): bool {
290 | return (bool)$this->_getConfig('processing');
291 | }
292 |
293 | /**
294 | * Setter method.
295 | * Enable or disable the display of a 'processing' indicator when the table is being processed (e.g. a sort). This
296 | * is particularly useful for tables with large amounts of data where it can take a noticeable amount of time to
297 | * sort the entries.
298 | *
299 | * @param bool $processing
300 | * @return \DataTables\Table\Option\MainOption
301 | * @link https://datatables.net/reference/option/processing
302 | */
303 | public function setProcessing(bool $processing): MainOption {
304 | $this->_setConfig('processing', $processing);
305 |
306 | return $this->getMainOption();
307 | }
308 |
309 | /**
310 | * Checker method.
311 | * Enable horizontal scrolling. When a table is too wide to fit into a certain layout, or you have a large number
312 | * of columns in the table, you can enable horizontal (x) scrolling to show the table in a viewport, which can be
313 | * scrolled.
314 | *
315 | * This property can be true which will allow the table to scroll horizontally when needed (recommended), or any
316 | * CSS unit, or a number (in which case it will be treated as a pixel measurement).
317 | *
318 | * @link https://datatables.net/reference/option/scrollX
319 | * @return bool
320 | */
321 | public function isScrollX(): bool {
322 | return (bool)$this->_getConfig('scrollX');
323 | }
324 |
325 | /**
326 | * Setter method.
327 | * Enable horizontal scrolling. When a table is too wide to fit into a certain layout, or you have a large number
328 | * of columns in the table, you can enable horizontal (x) scrolling to show the table in a viewport, which can be
329 | * scrolled.
330 | *
331 | * This property can be true which will allow the table to scroll horizontally when needed (recommended), or any
332 | * CSS unit, or a number (in which case it will be treated as a pixel measurement).
333 | *
334 | * @param bool $scrollX
335 | * @return \DataTables\Table\Option\MainOption
336 | * @link https://datatables.net/reference/option/scrollX
337 | */
338 | public function setScrollX(bool $scrollX): MainOption {
339 | $this->_setConfig('scrollX', $scrollX);
340 |
341 | return $this->getMainOption();
342 | }
343 |
344 | /**
345 | * Getter method.
346 | * Enable vertical scrolling. Vertical scrolling will constrain the DataTable to the given height, and enable
347 | * scrolling for any data which overflows the current viewport. This can be used as an alternative to paging to
348 | * display a lot of data in a small area (although paging and scrolling can both be enabled at the same time if
349 | * desired).
350 | *
351 | * The value given here can be any CSS unit, or a number (in which case it will be treated as a pixel measurement)
352 | * and is applied to the table body (i.e. it does not take into account the header or footer height directly).
353 | *
354 | * @link https://datatables.net/reference/option/scrollY
355 | * @return string
356 | */
357 | public function getScrollY(): ?string {
358 | return (string)$this->_getConfig('scrollY');
359 | }
360 |
361 | /**
362 | * Setter method.
363 | * Enable vertical scrolling. Vertical scrolling will constrain the DataTable to the given height, and enable
364 | * scrolling for any data which overflows the current viewport. This can be used as an alternative to paging to
365 | * display a lot of data in a small area (although paging and scrolling can both be enabled at the same time if
366 | * desired).
367 | *
368 | * The value given here can be any CSS unit, or a number (in which case it will be treated as a pixel measurement)
369 | * and is applied to the table body (i.e. it does not take into account the header or footer height directly).
370 | *
371 | * @param string $scrollY
372 | * @return \DataTables\Table\Option\MainOption
373 | * @link https://datatables.net/reference/option/scrollY
374 | */
375 | public function setScrollY(?string $scrollY): MainOption {
376 | $this->_setConfig('scrollY', $scrollY);
377 |
378 | return $this->getMainOption();
379 | }
380 |
381 | /**
382 | * Checker method.
383 | * This option allows the search abilities of DataTables to be enabled or disabled. Searching in DataTables is
384 | * "smart" in that it allows the end user to input multiple words (space separated) and will match a row containing
385 | * those words, even if not in the order that was specified (this allow matching across multiple columns).
386 | *
387 | * Please be aware that technically the search in DataTables is actually a filter, since it is subtractive,
388 | * removing data from the data set as the input becomes more complex. It is named "search" here, and else where in
389 | * the DataTables API for consistency and to ensure there are no conflicts with other methods of a similar name
390 | * (specific the filter() API method).
391 | *
392 | * Note that if you wish to use the search abilities of DataTables this must remain true - to remove the default
393 | * search input box whilst retaining searching abilities (for example you might use the search() method), use the
394 | * dom option.
395 | *
396 | * @link https://datatables.net/reference/option/searching
397 | * @return bool
398 | */
399 | public function isSearching(): bool {
400 | return (bool)$this->_getConfig('searching');
401 | }
402 |
403 | /**
404 | * Setter method.
405 | * This option allows the search abilities of DataTables to be enabled or disabled. Searching in DataTables is
406 | * "smart" in that it allows the end user to input multiple words (space separated) and will match a row containing
407 | * those words, even if not in the order that was specified (this allow matching across multiple columns).
408 | *
409 | * Please be aware that technically the search in DataTables is actually a filter, since it is subtractive,
410 | * removing data from the data set as the input becomes more complex. It is named "search" here, and else where in
411 | * the DataTables API for consistency and to ensure there are no conflicts with other methods of a similar name
412 | * (specific the filter() API method).
413 | *
414 | * Note that if you wish to use the search abilities of DataTables this must remain true - to remove the default
415 | * search input box whilst retaining searching abilities (for example you might use the search() method), use the
416 | * dom option.
417 | *
418 | * @param bool $searching
419 | * @return \DataTables\Table\Option\MainOption
420 | * @link https://datatables.net/reference/option/searching
421 | */
422 | public function setSearching(bool $searching): MainOption {
423 | $this->_setConfig('searching', $searching);
424 |
425 | return $this->getMainOption();
426 | }
427 |
428 | /**
429 | * Checker method.
430 | * DataTables has two fundamental modes of operation:
431 | * - Client-side processing - where filtering, paging and sorting calculations are all performed in the
432 | * web-browser.
433 | * - Server-side processing - where filtering, paging and sorting calculations are all performed by a server.
434 | *
435 | * By default DataTables operates in client-side processing mode, but can be switched to server-side processing
436 | * mode using this option. Server-side processing is useful when working with large data sets (typically >50'000
437 | * records) as it means a database engine can be used to perform the sorting etc calculations - operations that
438 | * modern database engines are highly optimised for, allowing use of DataTables with massive data sets (millions
439 | * of rows).
440 | *
441 | * When operating in server-side processing mode, DataTables will send parameters to the server indicating what
442 | * data it needs (what page, what filters are applied etc), and also expects certain parameters back in order that
443 | * it has all the information required to display the table. The client-server communication protocol DataTables
444 | * uses is detailed in the DataTables documentation.
445 | *
446 | * @link https://datatables.net/reference/option/serverSide
447 | * @return bool
448 | */
449 | public function isServerSide(): bool {
450 | return (bool)$this->_getConfig('serverSide');
451 | }
452 |
453 | /**
454 | * Setter method.
455 | * DataTables has two fundamental modes of operation:
456 | * - Client-side processing - where filtering, paging and sorting calculations are all performed in the
457 | * web-browser.
458 | * - Server-side processing - where filtering, paging and sorting calculations are all performed by a server.
459 | *
460 | * By default DataTables operates in client-side processing mode, but can be switched to server-side processing
461 | * mode using this option. Server-side processing is useful when working with large data sets (typically >50'000
462 | * records) as it means a database engine can be used to perform the sorting etc calculations - operations that
463 | * modern database engines are highly optimised for, allowing use of DataTables with massive data sets (millions
464 | * of rows).
465 | *
466 | * When operating in server-side processing mode, DataTables will send parameters to the server indicating what
467 | * data it needs (what page, what filters are applied etc), and also expects certain parameters back in order that
468 | * it has all the information required to display the table. The client-server communication protocol DataTables
469 | * uses is detailed in the DataTables documentation.
470 | *
471 | * @param bool $serverSide
472 | * @return \DataTables\Table\Option\MainOption
473 | * @link https://datatables.net/reference/option/serverSide
474 | */
475 | public function setServerSide(bool $serverSide): MainOption {
476 | if ($serverSide === false) {
477 | throw new FatalErrorException("By the plugin business rule, you can't change this option.");
478 | }
479 | $this->_setConfig('serverSide', $serverSide);
480 |
481 | return $this->getMainOption();
482 | }
483 |
484 | /**
485 | * Checker method.
486 | * Enable or disable state saving. When enabled aDataTables will store state information such as pagination
487 | * position, display length, filtering and sorting. When the end user reloads the page the table's state will be
488 | * altered to match what they had previously set up.
489 | *
490 | * Data storage for the state information in the browser is performed by use of the localStorage or sessionStorage
491 | * HTML5 APIs. The stateDuration indicated to DataTables which API should be used (localStorage: 0 or greater, or
492 | * sessionStorage: -1).
493 | *
494 | * To be able to uniquely identify each table's state data, information is stored using a combination of the
495 | * table's DOM id and the current page's pathname. If the table's id changes, or the page URL changes, the state
496 | * information will be lost.
497 | *
498 | * Please note that the use of the HTML5 APIs for data storage means that the built in state saving option will not
499 | * work with IE6/7 as these browsers do not support these APIs. Alternative options of using cookies or saving the
500 | * state on the server through Ajax can be used through the stateSaveCallback and stateLoadCallback options.
501 | *
502 | * @link https://datatables.net/reference/option/stateSave
503 | * @return bool
504 | */
505 | public function isStateSave(): bool {
506 | return (bool)$this->_getConfig('stateSave');
507 | }
508 |
509 | /**
510 | * Setter method.
511 | * Enable or disable state saving. When enabled aDataTables will store state information such as pagination
512 | * position, display length, filtering and sorting. When the end user reloads the page the table's state will be
513 | * altered to match what they had previously set up.
514 | *
515 | * Data storage for the state information in the browser is performed by use of the localStorage or sessionStorage
516 | * HTML5 APIs. The stateDuration indicated to DataTables which API should be used (localStorage: 0 or greater, or
517 | * sessionStorage: -1).
518 | *
519 | * To be able to uniquely identify each table's state data, information is stored using a combination of the
520 | * table's DOM id and the current page's pathname. If the table's id changes, or the page URL changes, the state
521 | * information will be lost.
522 | *
523 | * Please note that the use of the HTML5 APIs for data storage means that the built in state saving option will not
524 | * work with IE6/7 as these browsers do not support these APIs. Alternative options of using cookies or saving the
525 | * state on the server through Ajax can be used through the stateSaveCallback and stateLoadCallback options.
526 | *
527 | * @param bool $stateSave
528 | * @return \DataTables\Table\Option\MainOption
529 | * @link https://datatables.net/reference/option/stateSave
530 | */
531 | public function setStateSave(bool $stateSave): MainOption {
532 | $this->_setConfig('stateSave', $stateSave);
533 |
534 | return $this->getMainOption();
535 | }
536 |
537 | }
538 |
--------------------------------------------------------------------------------
/src/Table/QueryBaseState.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table;
13 |
14 | /**
15 | * Class QueryBaseState
16 | *
17 | * @author Allan Carvalho
18 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
19 | * @link https://github.com/allanmcarvalho/cakephp-datatables
20 | */
21 | final class QueryBaseState {
22 |
23 | /**
24 | * @var array
25 | */
26 | private $_containItems = [];
27 |
28 | /**
29 | * @var array
30 | */
31 | private $_selectItems = [];
32 |
33 | /**
34 | * @var array
35 | */
36 | private $_selectAllExceptItems = [];
37 |
38 | /**
39 | * @var array
40 | */
41 | private $_leftJoinWithItems = [];
42 |
43 | /**
44 | * @var array
45 | */
46 | private $_innerJoinWithItems = [];
47 |
48 | /**
49 | * @var array
50 | */
51 | private $_notMatchingItems = [];
52 |
53 | /**
54 | * @var array
55 | */
56 | private $_orderAscItems = [];
57 |
58 | /**
59 | * @var array
60 | */
61 | private $_orderDescItems = [];
62 |
63 | /**
64 | * @var array
65 | */
66 | private $_whereItems = [];
67 |
68 | /**
69 | * @var array
70 | */
71 | private $_whereInListItems = [];
72 |
73 | /**
74 | * @var array
75 | */
76 | private $_whereNotNullItems = [];
77 |
78 | /**
79 | * @var array
80 | */
81 | private $_whereNotInListItems = [];
82 |
83 | /**
84 | * @var array
85 | */
86 | private $_whereNullItems = [];
87 |
88 | /**
89 | * @var array
90 | */
91 | private $_andWhereItems = [];
92 |
93 | /**
94 | * @var array
95 | */
96 | private $_urlWhereItems = [];
97 |
98 | /**
99 | * Return
100 | *
101 | * @return array
102 | */
103 | public function getArray(): array {
104 | return [
105 | 'contain' => $this->_containItems,
106 | 'select' => $this->_selectItems,
107 | 'selectAllExcept' => $this->_selectAllExceptItems,
108 | 'leftJoinWith' => $this->_leftJoinWithItems,
109 | 'innerJoinWith' => $this->_innerJoinWithItems,
110 | 'notMatching' => $this->_notMatchingItems,
111 | 'orderAsc' => $this->_orderAscItems,
112 | 'orderDesc' => $this->_orderDescItems,
113 | 'where' => $this->_whereItems,
114 | 'whereInList' => $this->_whereInListItems,
115 | 'whereNotNull' => $this->_whereNotNullItems,
116 | 'whereNotInList' => $this->_whereNotInListItems,
117 | 'whereNull' => $this->_whereNullItems,
118 | 'andWhere' => $this->_andWhereItems,
119 | 'urlWhere' => $this->_urlWhereItems,
120 | ];
121 | }
122 |
123 | /**
124 | * @param array|string $associations List of table aliases to be queried.
125 | * @param callable|bool $override The query builder for the association, or
126 | * if associations is an array, a bool on whether to override previous list
127 | * with the one passed
128 | * defaults to merging previous list with the new one.
129 | * @return $this
130 | * @see \Cake\ORM\Query::contain()
131 | */
132 | public function contain($associations, $override = false): self {
133 | $queryBuilder = null;
134 | if ($override === true) {
135 | $this->_containItems = [];
136 | }
137 | $queryBuilder = null;
138 | if (is_callable($override)) {
139 | $queryBuilder = $override;
140 | }
141 | if ($associations) {
142 | $this->_containItems[] = [
143 | 'associations' => $associations,
144 | 'queryBuilder' => $queryBuilder,
145 | ];
146 | }
147 | return $this;
148 | }
149 |
150 | /**
151 | * @param array|\Cake\Database\ExpressionInterface|callable|string|\Cake\ORM\Table|\Cake\ORM\Association $fields Fields
152 | * to be added to the list.
153 | * @param bool $overwrite whether to reset fields with passed list or not
154 | * @return $this
155 | * @see \Cake\ORM\Query::select()
156 | */
157 | public function select($fields, bool $overwrite = false): self {
158 | if ($overwrite === true) {
159 | $this->_selectItems = [];
160 | }
161 | $this->_selectItems[] = [
162 | 'fields' => $fields,
163 | ];
164 | return $this;
165 | }
166 |
167 | /**
168 | * @param \Cake\ORM\Table|\Cake\ORM\Association $table The table to use to get an array of columns
169 | * @param string[] $excludedFields The un-aliased column names you do not want selected from $table
170 | * @param bool $overwrite Whether to reset/remove previous selected fields
171 | * @return $this
172 | * @throws \InvalidArgumentException If Association|Table is not passed in first argument
173 | * @see \Cake\ORM\Query::selectAllExcept()
174 | */
175 | public function selectAllExcept($table, array $excludedFields, bool $overwrite = false): self {
176 | if ($overwrite === true) {
177 | $this->_selectAllExceptItems = [];
178 | }
179 | $this->_selectAllExceptItems[] = [
180 | 'table' => $table,
181 | 'excludedFields' => $excludedFields,
182 | ];
183 | return $this;
184 | }
185 |
186 | /**
187 | * @param string $assoc The association to join with
188 | * @param callable|null $builder a function that will receive a pre-made query object
189 | * that can be used to add custom conditions or selecting some fields
190 | * @return $this
191 | * @see \Cake\ORM\Query::leftJoinWith()
192 | */
193 | public function leftJoinWith(string $assoc, ?callable $builder = null): self {
194 | $this->_leftJoinWithItems[] = [
195 | 'assoc' => $assoc,
196 | 'builder' => $builder,
197 | ];
198 | return $this;
199 | }
200 |
201 | /**
202 | * @param string $assoc The association to join with
203 | * @param callable|null $builder a function that will receive a pre-made query object
204 | * that can be used to add custom conditions or selecting some fields
205 | * @return $this
206 | * @see \Cake\ORM\Query::innerJoinWith()
207 | */
208 | public function innerJoinWith(string $assoc, ?callable $builder = null): self {
209 | $this->_innerJoinWithItems[] = [
210 | 'assoc' => $assoc,
211 | 'builder' => $builder,
212 | ];
213 | return $this;
214 | }
215 |
216 | /**
217 | * @param string $assoc The association to filter by
218 | * @param callable|null $builder a function that will receive a pre-made query object
219 | * that can be used to add custom conditions or selecting some fields
220 | * @return $this
221 | * @see \Cake\ORM\Query::notMatching()
222 | */
223 | public function notMatching(string $assoc, ?callable $builder = null): self {
224 | $this->_notMatchingItems[] = [
225 | 'assoc' => $assoc,
226 | 'builder' => $builder,
227 | ];
228 | return $this;
229 | }
230 |
231 | /**
232 | * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on.
233 | * @param bool $overwrite Whether or not to reset the order clauses.
234 | * @return $this
235 | * @see \Cake\ORM\Query::orderAsc()
236 | */
237 | public function orderAsc($field, bool $overwrite = false): self {
238 | if ($overwrite === true) {
239 | $this->_orderAscItems = [];
240 | }
241 | $this->_orderAscItems[] = [
242 | 'field' => $field,
243 | ];
244 | return $this;
245 | }
246 |
247 | /**
248 | * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on.
249 | * @param bool $overwrite Whether or not to reset the order clauses.
250 | * @return $this
251 | * @see \Cake\ORM\Query::orderDesc()
252 | */
253 | public function orderDesc($field, bool $overwrite = false): self {
254 | if ($overwrite === true) {
255 | $this->_orderDescItems = [];
256 | }
257 | $this->_orderDescItems[] = [
258 | 'field' => $field,
259 | ];
260 | return $this;
261 | }
262 |
263 | /**
264 | * @param string|array|\Cake\Database\ExpressionInterface|\Closure|null $conditions The conditions to filter on.
265 | * @param array $types associative array of type names used to bind values to query
266 | * @param bool $overwrite whether to reset conditions with passed list or not
267 | * @return $this
268 | * @see \Cake\ORM\Query::where()
269 | */
270 | public function where($conditions = null, array $types = [], bool $overwrite = false): self {
271 | if ($overwrite === true) {
272 | $this->_whereItems = [];
273 | }
274 | $this->_whereItems[] = [
275 | 'conditions' => $conditions,
276 | 'types' => $types,
277 | ];
278 | return $this;
279 | }
280 |
281 | /**
282 | * @param string $field Field
283 | * @param array $values Array of values
284 | * @param array $options Options
285 | * @return $this
286 | * @see \Cake\ORM\Query::whereInList()
287 | */
288 | public function whereInList(string $field, array $values, array $options = []): self {
289 | $this->_whereInListItems[] = [
290 | 'field' => $field,
291 | 'values' => $values,
292 | 'options' => $options,
293 | ];
294 | return $this;
295 | }
296 |
297 | /**
298 | * @param array|string|\Cake\Database\ExpressionInterface $fields A single field or expressions or a list of them
299 | * that should be not null.
300 | * @return $this
301 | * @see \Cake\ORM\Query::whereNotNull()
302 | */
303 | public function whereNotNull($fields): self {
304 | $this->_whereNotNullItems[] = [
305 | 'fields' => $fields,
306 | ];
307 | return $this;
308 | }
309 |
310 | /**
311 | * @param string $field Field
312 | * @param array $values Array of values
313 | * @param array $options Options
314 | * @return $this
315 | * @see \Cake\ORM\Query::whereNotInList()
316 | */
317 | public function whereNotInList(string $field, array $values, array $options = []): self {
318 | $this->_whereNotInListItems[] = [
319 | 'field' => $field,
320 | 'values' => $values,
321 | 'options' => $options,
322 | ];
323 | return $this;
324 | }
325 |
326 | /**
327 | * @param array|string|\Cake\Database\ExpressionInterface $fields A single field or expressions or a list of them
328 | * that should be null.
329 | * @return $this
330 | * @see \Cake\ORM\Query::whereNull()
331 | */
332 | public function whereNull($fields): self {
333 | $this->_whereNullItems[] = [
334 | 'fields' => $fields,
335 | ];
336 | return $this;
337 | }
338 |
339 | /**
340 | * @param string|array|\Cake\Database\ExpressionInterface|\Closure $conditions The conditions to add with AND.
341 | * @param array $types associative array of type names used to bind values to query
342 | * @return $this
343 | * @see \Cake\ORM\Query::andWhere()
344 | */
345 | public function andWhere($conditions, array $types = []): self {
346 | $this->_andWhereItems[] = [
347 | 'conditions' => $conditions,
348 | 'types' => $types,
349 | ];
350 | return $this;
351 | }
352 |
353 | /**
354 | * Add a condition for a specific URL
355 | *
356 | * @param string|array|\Cake\Database\ExpressionInterface|\Closure|null $conditions The conditions to filter on.
357 | * @param string|array|\Psr\Http\Message\UriInterface|null $url An array specifying any of the following:
358 | * @return $this
359 | */
360 | public function urlWhere($conditions, $url): self {
361 | $this->_urlWhereItems[] = [
362 | 'conditions' => $conditions,
363 | 'url' => $url,
364 | ];
365 | return $this;
366 | }
367 |
368 | }
369 |
--------------------------------------------------------------------------------
/src/Table/Tables.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Table;
13 |
14 | use Cake\Error\FatalErrorException;
15 | use Cake\ORM\Table;
16 | use Cake\ORM\TableRegistry;
17 |
18 | /**
19 | * Class Tables
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | abstract class Tables {
26 |
27 | /**
28 | * The database table name that will be used to load the DataTables ORM table.
29 | *
30 | * @var string
31 | */
32 | protected $_ormTableName;
33 |
34 | /**
35 | * @var \Cake\ORM\Table
36 | */
37 | private $_ormTable;
38 |
39 | public function __construct() {
40 | $className = get_called_class();
41 | $classShortName = explode('\\', get_called_class());
42 | $classShortName = array_pop($classShortName);
43 | if (substr($classShortName, -6, 6) !== 'Tables') {
44 | throw new FatalErrorException("The class '$className' must have the name ending with 'Tables'");
45 | }
46 | if (empty($this->_ormTableName)) {
47 | $this->_ormTableName = substr_replace($classShortName, '', -6, 6);
48 | }
49 | }
50 |
51 | /**
52 | * @return \Cake\ORM\Table
53 | */
54 | public function getOrmTable(): Table {
55 | return TableRegistry::getTableLocator()->get($this->_ormTableName);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/Tools/Builder.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Tools;
13 |
14 | use Cake\Core\Configure;
15 | use Cake\Error\FatalErrorException;
16 | use Cake\Utility\Inflector;
17 | use DataTables\StorageEngine\StorageEngineInterface;
18 | use DataTables\Table\Columns;
19 | use DataTables\Table\ConfigBundle;
20 | use DataTables\Table\Option\MainOption;
21 | use DataTables\Table\QueryBaseState;
22 | use DataTables\Table\Tables;
23 | use InvalidArgumentException;
24 |
25 | /**
26 | * Class Tools
27 | *
28 | * @author Allan Carvalho
29 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
30 | * @link https://github.com/allanmcarvalho/cakephp-datatables
31 | */
32 | class Builder {
33 |
34 | /**
35 | * Storage a instance of object.
36 | *
37 | * @var self
38 | */
39 | public static $instance;
40 |
41 | /**
42 | * Return a instance of builder object.
43 | *
44 | * @return \DataTables\Tools\Builder
45 | */
46 | public static function getInstance(): Builder {
47 | if (static::$instance === null) {
48 | static::$instance = new self();
49 | }
50 | return static::$instance;
51 | }
52 |
53 | /**
54 | * Get or build a ConfigBundle.
55 | *
56 | * @param string $tableAndConfig A Tables class plus config method that you want to render concatenated by '::'. Eg.: 'Foo::main'.
57 | * @param bool $cache If true will try to get from cache.
58 | * @return \DataTables\Table\ConfigBundle
59 | * @throws \ReflectionException
60 | */
61 | public function getConfigBundle(string $tableAndConfig, bool $cache = true): ConfigBundle {
62 | $exploded = explode('::', $tableAndConfig);
63 | if (count($exploded) !== 2) {
64 | throw new InvalidArgumentException('Table param must be a concatenation of Tables class and config. Eg.: Foo::method.');
65 | }
66 | $storageEngine = $this->getStorageEngine();
67 | $tablesClass = $exploded[0];
68 | $configMethod = $exploded[1];
69 | $tablesClassWithNameSpace = Configure::read('App.namespace') . '\\DataTables\\Tables\\' . $tablesClass . 'Tables';
70 | $md5 = Functions::getInstance()->getClassAndVersionMd5($tablesClassWithNameSpace);
71 | $cacheKey = Inflector::underscore(str_replace('::', '_', $tableAndConfig));
72 |
73 | $configBundle = null;
74 | if ($cache === true && $storageEngine->exists($cacheKey)) {
75 | /** @var \DataTables\Table\ConfigBundle $configBundle */
76 | $configBundle = $storageEngine->read($cacheKey);
77 | }
78 | if (empty($configBundle) && !$configBundle instanceof ConfigBundle) {
79 | $configBundle = $this->buildConfigBundle($tablesClassWithNameSpace, $configMethod, $md5);
80 | }
81 | if ($cache && !$storageEngine->save($cacheKey, $configBundle)) {
82 | throw new FatalErrorException('Unable to save the ConfigBundle cache.');
83 | }
84 |
85 | return $configBundle;
86 | }
87 |
88 | /**
89 | * Build a ConfigBundle class
90 | *
91 | * @param string $tablesClassWithNameSpace Tables class with full namespace.
92 | * @param string $configMethod The method that will be called.
93 | * @param string $md5 Md5 verifier used in the cache.
94 | * @return \DataTables\Table\ConfigBundle
95 | */
96 | public function buildConfigBundle(string $tablesClassWithNameSpace, string $configMethod, string $md5): ConfigBundle {
97 | $tables = static::getInstance()->buildTables($tablesClassWithNameSpace, $configMethod);
98 | $queryBaseState = static::getInstance()->buildQueryBaseState($tables);
99 | $columns = static::getInstance()->buildColumns($tables);
100 | $options = static::getInstance()->buildOptions($tables);
101 | $configBundle = new ConfigBundle($md5, $queryBaseState, $columns, $options);
102 | $tables->{$configMethod . 'Config'}($configBundle);
103 | return $configBundle;
104 | }
105 |
106 | /**
107 | * Get the Tables class.
108 | *
109 | * @param string $tablesClassWithNameSpace Tables class with full namespace.
110 | * @param string $configMethod The method that will be called.
111 | * @return \DataTables\Table\Tables
112 | */
113 | public function buildTables(string $tablesClassWithNameSpace, string $configMethod): Tables {
114 | /** @var \DataTables\Table\Tables $tables */
115 | $tables = new $tablesClassWithNameSpace();
116 | if (empty($tables)) {
117 | throw new FatalErrorException("Tables class '$tablesClassWithNameSpace' not found.");
118 | }
119 | if (!method_exists($tables, $configMethod . 'Config')) {
120 | throw new FatalErrorException("Config method '{$configMethod}Config' don't exist in '$tablesClassWithNameSpace'.");
121 | }
122 |
123 | return $tables;
124 | }
125 |
126 | /**
127 | * Get the QueryBaseState class used in the DataTables table.
128 | *
129 | * @param \DataTables\Table\Tables $table Tables class instance.
130 | * @return \DataTables\Table\QueryBaseState
131 | */
132 | private function buildQueryBaseState(Tables $table) {
133 | return new QueryBaseState();
134 | }
135 |
136 | /**
137 | * Get the Columns class used in the DataTables table.
138 | *
139 | * @param \DataTables\Table\Tables $table Tables class instance.
140 | * @return \DataTables\Table\Columns
141 | */
142 | private function buildColumns(Tables $table): Columns {
143 | return new Columns($table);
144 | }
145 |
146 | /**
147 | * Get the JsOptions class used in the DataTables table.
148 | *
149 | * @param \DataTables\Table\Tables $table Tables class instance.
150 | * @return \DataTables\Table\Option\MainOption
151 | */
152 | private function buildOptions(Tables $table): MainOption {
153 | return new MainOption();
154 | }
155 |
156 | /**
157 | * Get the configured storage engine.
158 | *
159 | * @return \DataTables\StorageEngine\StorageEngineInterface
160 | */
161 | public function getStorageEngine(): StorageEngineInterface {
162 | $class = Configure::read('DataTables.StorageEngine.class');
163 | return new $class();
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/Tools/Functions.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Tools;
13 |
14 | use Cake\Routing\Router;
15 | use Cake\Utility\Hash;
16 | use Cake\Utility\Inflector;
17 | use InvalidArgumentException;
18 | use ReflectionClass;
19 |
20 | /**
21 | * Class Functions
22 | *
23 | * @author Allan Carvalho
24 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
25 | * @link https://github.com/allanmcarvalho/cakephp-datatables
26 | */
27 | class Functions {
28 |
29 | /**
30 | * Storage a instance of object.
31 | *
32 | * @var self
33 | */
34 | public static $instance;
35 |
36 | /**
37 | * Return a instance of builder object.
38 | *
39 | * @return \DataTables\Tools\Functions
40 | */
41 | public static function getInstance(): Functions {
42 | if (static::$instance === null) {
43 | static::$instance = new self();
44 | }
45 | return static::$instance;
46 | }
47 |
48 | /**
49 | * Get first key of array.
50 | *
51 | * @param array $array
52 | * @return int|string|null
53 | */
54 | public function arrayKeyFirst(array $array){
55 | reset($array);
56 | return key($array);
57 | }
58 |
59 | /**
60 | * Get last key of array.
61 | *
62 | * @param array $array
63 | * @return int|string|null
64 | */
65 | public function arrayKeyLast(array $array){
66 | end($array);
67 | return key($array);
68 | }
69 |
70 | /**
71 | * Check if passed url is the same as current url.
72 | *
73 | * @param array $url
74 | * @return bool
75 | */
76 | public function isSameAsCurrentUrl(array $url = []): bool {
77 | $currentUrlMd5 = $this->getUrlMd5(
78 | Router::getRequest()->getParam('controller'),
79 | Router::getRequest()->getParam('action'),
80 | Router::getRequest()->getQuery(),
81 | Router::getRequest()->getParam('prefix'),
82 | Router::getRequest()->getParam('pass')
83 | );
84 | $controller = Hash::get($url, 'controller', Router::getRequest()->getParam('controller'));
85 | $action = Hash::get($url, 'action', Router::getRequest()->getParam('action'));
86 | $query = Hash::get($url, '?', Router::getRequest()->getQuery());
87 | $prefix = Hash::get($url, 'prefix', Router::getRequest()->getParam('prefix'));
88 | if (!is_array($query)) {
89 | throw new InvalidArgumentException('Query param must be an array.');
90 | }
91 |
92 | $url = Hash::remove($url, 'controller');
93 | $url = Hash::remove($url, 'action');
94 | $url = Hash::remove($url, '?');
95 | $url = Hash::remove($url, 'prefix');
96 | $urlMd5 = $this->getUrlMd5($controller, $action, $query, $prefix, $url);
97 |
98 | return $currentUrlMd5 === $urlMd5;
99 | }
100 |
101 | /**
102 | * Check if passed params are in current url.
103 | *
104 | * @param array $url
105 | * @return bool
106 | */
107 | public function isInCurrentUrl(array $url = []): bool {
108 | $currentController = Router::getRequest()->getParam('controller');
109 | $currentAction = Router::getRequest()->getParam('action');
110 | $currentQuery = Router::getRequest()->getQuery();
111 | $currentPrefix = Router::getRequest()->getParam('prefix');
112 | $currentPass = Router::getRequest()->getParam('pass');
113 | $controller = Hash::get($url, 'controller', $currentController);
114 | $action = Hash::get($url, 'action', $currentAction);
115 | $query = Hash::get($url, '?', []);
116 | $prefix = Hash::get($url, 'prefix', $currentPrefix);
117 | if (!is_array($query)) {
118 | throw new InvalidArgumentException('Query param must be an array.');
119 | }
120 | $url = Hash::remove($url, 'controller');
121 | $url = Hash::remove($url, 'action');
122 | $url = Hash::remove($url, '?');
123 | $url = Hash::remove($url, 'prefix');
124 | $pass = $url;
125 | if ($controller !== $currentController || $action !== $currentAction || $prefix !== $currentPrefix) {
126 | return false;
127 | }
128 | foreach ($pass as $key => $passItem) {
129 | if (empty($currentPass[$key]) || (!empty($currentPass[$key]) && $passItem !== $currentPass[$key])) {
130 | return false;
131 | }
132 | }
133 | foreach ($query as $key => $queryItem) {
134 | if (empty($currentQuery[$key]) || (!empty($currentQuery[$key]) && $queryItem !== $currentQuery[$key])) {
135 | return false;
136 | }
137 | }
138 | return true;
139 | }
140 |
141 | /**
142 | * Convert Url in md5 string
143 | *
144 | * @param string $controller
145 | * @param string $action
146 | * @param array $query
147 | * @param string|null $prefix
148 | * @param array $pass
149 | * @return string
150 | */
151 | private function getUrlMd5(string $controller, string $action, array $query = [], ?string $prefix = null, array $pass = []): string {
152 | $md5Items = [];
153 | $md5Items[] = Inflector::camelize($controller);
154 | $md5Items[] = Inflector::camelize($action);
155 | $md5Items[] = !empty($prefix) ? Inflector::camelize($prefix) : '_EMPTY_';
156 | ksort($pass);
157 | foreach ($pass as $key => $passItem) {
158 | $md5Items[] = Inflector::camelize("$key=$passItem");
159 | }
160 | ksort($query);
161 | foreach ($query as $key => $queryItem) {
162 | $md5Items[] = Inflector::camelize("$key=$queryItem");
163 | }
164 | return md5(implode('::', $md5Items));
165 | }
166 |
167 | /**
168 | * Return current package version.
169 | *
170 | * @return string
171 | */
172 | public function getPluginCurrentVersion(): string {
173 | $version = '0';
174 | $packages = json_decode(file_get_contents(ROOT . DS . 'vendor' . DS . 'composer' . DS . 'installed.json'));
175 | foreach ($packages as $package) {
176 | if ($package->name === 'allanmcarvalho/cakephp-datatables') {
177 | $version = $package->version;
178 | }
179 | }
180 | return $version;
181 | }
182 |
183 | /**
184 | * Return the class md5
185 | *
186 | * @param string $classWithNameSpace Class name with namespace.
187 | * @return string Md5 string
188 | * @throws \ReflectionException
189 | */
190 | public function getClassMd5(string $classWithNameSpace): string {
191 | return md5_file((new ReflectionClass($classWithNameSpace))->getFileName());
192 | }
193 |
194 | /**
195 | * @param string $classWithNameSpace
196 | * @throws \ReflectionException
197 | * @return string
198 | */
199 | public function getClassAndVersionMd5(string $classWithNameSpace): string {
200 | $classMd5 = $this->getClassMd5($classWithNameSpace);
201 | $versionMd5 = md5($this->getPluginCurrentVersion());
202 | return md5($classMd5 . $versionMd5);
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/src/Tools/Js.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Tools;
13 |
14 | use MatthiasMullie\Minify\JS as JsMinify;
15 |
16 | /**
17 | * Class Js
18 | *
19 | * @author Allan Carvalho
20 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
21 | * @link https://github.com/allanmcarvalho/cakephp-datatables
22 | */
23 | class Js {
24 |
25 | /**
26 | * Minify a js script.
27 | *
28 | * @param string $content Js to be minified.
29 | * @return string Minified Js.
30 | */
31 | public static function minifyFile(string $content): string {
32 | $minifyJs = new JsMinify();
33 | return $minifyJs->add($content)->minify();
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/Tools/Validator.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\Tools;
13 |
14 | use Cake\Error\FatalErrorException;
15 | use Cake\Utility\Text;
16 | use InvalidArgumentException;
17 |
18 | /**
19 | * Class Validator
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | class Validator {
26 |
27 | /**
28 | * Storage a instance of object.
29 | *
30 | * @var self
31 | */
32 | public static $instance;
33 |
34 | /**
35 | * Return a instance of builder object.
36 | *
37 | * @return \DataTables\Tools\Validator
38 | */
39 | public static function getInstance(): Validator {
40 | if (static::$instance === null) {
41 | static::$instance = new self();
42 | }
43 | return static::$instance;
44 | }
45 |
46 | /**
47 | * Check if the array keys and values are correct.
48 | *
49 | * @param array $array
50 | * @param string|array $allowedKeyTypes A allowed types for array key.
51 | * @param string|array $allowedValueTypes A allowed types for array value.
52 | * @param string|null $inString A string to make the error more friendly.
53 | * @return void
54 | */
55 | public function checkKeysValueTypesOrFail(?array $array, $allowedKeyTypes = [], $allowedValueTypes = [], string $inString = null): void {
56 | if (empty($array)) {
57 | return;
58 | }
59 | $allowedKeyTypesType = getType($allowedKeyTypes);
60 | if (!in_array($allowedKeyTypesType, ['array', 'string'])) {
61 | throw new FatalErrorException(sprintf('The $keyType type must be an array or string. Found : %s', $allowedKeyTypesType));
62 | } elseif ($allowedKeyTypesType === 'string') {
63 | $allowedKeyTypes = [$allowedKeyTypes];
64 | }
65 | $allowedValueTypesType = getType($allowedValueTypes);
66 | if (!in_array($allowedValueTypesType, ['array', 'string'])) {
67 | throw new FatalErrorException(sprintf('The $valueType type must be an array or string. Found : %s', $allowedValueTypesType));
68 | } elseif ($allowedValueTypesType === 'string') {
69 | $allowedValueTypes = [$allowedValueTypes];
70 | }
71 | foreach ($array as $key => $value) {
72 | $keyType = getType($key);
73 | $valueType = getType($value);
74 | if (!in_array($keyType, $allowedKeyTypes) && $allowedKeyTypes !== ['*']) {
75 | $needleString = str_replace(' and ', ' or ', Text::toList($allowedKeyTypes));
76 | throw new InvalidArgumentException("In $inString array, the keys always must be $needleString. key: $key.");
77 | }
78 | if (!in_array($valueType, $allowedValueTypes) && $allowedValueTypes !== ['*']) {
79 | $needleString = str_replace(' and ', ' or ', Text::toList($allowedValueTypes));
80 | throw new InvalidArgumentException("In $inString array, the record $key isn't $needleString. Found: '$valueType'.");
81 | }
82 | }
83 | }
84 |
85 | /**
86 | * Check if array size is right.
87 | *
88 | * @param array $array The array that will be checked.
89 | * @param int $expected The expected size.
90 | * @param string|null $message A custom exception message.
91 | * @return void
92 | */
93 | public function checkArraySizeOrFail(array $array, int $expected, ?string $message = null): void {
94 | $size = count($array);
95 | if ($size !== $expected) {
96 | if (empty($message)) {
97 | $message = "Wrong array size. Expected: '$expected'. Found: '$size'.";
98 | }
99 | throw new InvalidArgumentException($message);
100 | }
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/View/Cell/DataTablesCell.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\View\Cell;
13 |
14 | use Cake\View\Cell;
15 | use DataTables\Table\Columns;
16 |
17 | /**
18 | * Class DataTablesCell
19 | *
20 | * @author Allan Carvalho
21 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
22 | * @link https://github.com/allanmcarvalho/cakephp-datatables
23 | */
24 | class DataTablesCell extends Cell {
25 |
26 | /**
27 | * Method that return the table html structure.
28 | *
29 | * @param \DataTables\Table\Columns $columns Config that is a concatenation of Tables class and config method.
30 | * @return void
31 | */
32 | public function table(Columns $columns): void {
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/View/Helper/DataTablesHelper.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 |
12 | namespace DataTables\View\Helper;
13 |
14 | use Cake\Core\Configure;
15 | use Cake\View\Helper;
16 | use DataTables\Tools\Builder;
17 |
18 | /**
19 | * Class DataTablesHelper
20 | *
21 | * @author Allan Carvalho
22 | * @license MIT License https://github.com/allanmcarvalho/cakephp-datatables/blob/master/LICENSE
23 | * @link https://github.com/allanmcarvalho/cakephp-datatables
24 | */
25 | class DataTablesHelper extends Helper {
26 |
27 | /**
28 | * Default configuration.
29 | *
30 | * @var array
31 | */
32 | protected $_defaultConfig = [
33 | 'cache' => true,
34 | 'minify' => true,
35 | ];
36 |
37 | /**
38 | * @inheritDoc
39 | */
40 | public function initialize(array $config): void {
41 | parent::initialize($config);
42 | if (Configure::read('debug') === true) {
43 | $this->setConfig('cache', !(bool)Configure::read('DataTables.StorageEngine.disableWhenDebugOn'));
44 | }
45 | }
46 |
47 | /**
48 | * Render the table html structure of a DataTables configure.
49 | *
50 | * @param string $table A Tables class plus config method that you want to render concatenated by '::'. Eg.: 'Foo::main'.
51 | * @return string
52 | * @throws \ReflectionException
53 | */
54 | public function renderTable(string $table): string {
55 | $configBundle = Builder::getInstance()->getConfigBundle($table, $this->getConfig('cache'));
56 | return $configBundle->generateTableHtml($this->getView());
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/templates/cell/DataTables/table.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | declare(strict_types = 1);
11 | ?>
12 |
13 |