Skip to content
ypnos edited this page Oct 28, 2024 · 7 revisions

In this example you will learn how to use the DataTables plugin in the simplest fashion.

Installation

Add plugin

Use composer to install this plugin. Add the following repository and requirement to your composer.json:

"require": {
    "ypnos-web/cakephp-datatables": "dev-master"
}

Load plugin. Starting with CakePHP 3.7, add to your Application::bootstrap():

$this->addPlugin('DataTables');

In CakePHP 3.6, instead add to app/bootstrap.php:

Plugin::load('DataTables', ['bootstrap' => false, 'routes' => false]);

Add component and helper

For example in your AppController:

class AppController extends Controller
{
    
    public $helpers = [
        'DataTables' => [
            'className' => 'DataTables.DataTables'
        ]
    ];
    
    public function initialize()
    {
        $this->loadComponent('DataTables.DataTables');
    }
    
}

Step 3: Include assets

Include jQuery and jQuery DataTables scripts first and then the dataTables logic:

echo $this->Html->script([
	'*PATH*/jquery.min.js',
	'*PATH*/jquery.dataTables.min.js',
	'*PATH*/dataTables.bootstrap.min.js', // optional
	'DataTables.cakephp.dataTables.js',
]);

Include dataTables css:

echo $this->Html->css('*PATH*/dataTables.bootstrap.css');

If you don't use Bootstrap, see the DataTables documentation for which files to include instead. For FontAwesome, you might also want to have a look at this.

There is also a bunch of really helpful extensions for DataTables that we support, e.g. Scroller and Select.

Usage

There is two parts to DataTables from the Cake perspective:

  1. Adding a table with DataTables overlay in the view template
  2. Custom Controller logic to support DataTables AJAX requests

Create your table in the template

The DataTables helper provides a very easy method to create a table in your template:

public function table(string $id = 'datatable', array $dtOptions = [], array $htmlOptions = []) : string

This method generates a stub <table> element and corresponding javascript which lets DataTables generate both heading and body of the table for us.

In this example of a table showing users, we also provide initial data to display:

$options = [
	'ajax' => [
		'url' => $this->Url->build() // current controller, action, params
	],
	'data' => $data,
	'deferLoading' => $data->count(), // https://datatables.net/reference/option/deferLoading
	'columns' => [
		[
			'data' => 'id',
			'visible' => false,
			'searchable' => false,
		],
		[
			'title' => __('First Name'),
			'data' => 'firstname'
		],
		[
			'title' => __('Last Name'),
			'data' => 'lastname'
		],
		[
			'title' => __('Login name'),
			'data' => 'username',
			'className' => 'text-primary',
		],
		[
			'title' => __('Department'),
			'data' => 'department.name'
		],
	],
	'order' => [3, 'asc'], // order by username
];
echo $this->DataTables->table('users-table', $options, ['class' => 'table table-striped']);

We could also not provide the initial data which would lead to dataTables using AJAX from the beginning. In this case, we would also omit the deferLoading option.

Some notes on $dtOptions (second argument):

  1. The array consists of options to DataTables as per the DataTables options reference.
  2. All columns need data defined so that DataTables can read the correct variable from $data (which is provided in JSON format). The identifiers in data therefore correspond to how the data is accessible from the view variable.
  3. All columns need title defined if you do not provide a table heading in the HTML.
  4. You can determine which columns should be searchable, orderable or even visible. An invisible column can be helpful for custom javascript callbacks which are discussed below.
  5. Some options are default in the plugin, e.g. serverSide = true, which you can overwrite if needed.

In the $htmlOptions we provided Bootstrap CSS classes. The class dataTable is always added.

Process DataTables AJAX requests (no filtering, searching yet!)

As mentioned earlier, in the default configuration and above example, DataTables will serve the initially provided data first, but let the server process any new requests (e.g. pagination or scrolling using Scroller).

For this, in the controller, we replace our regular find operation with the wrapper provided by the plugin:

$data = $this->DataTables->find('Users', 'all', [
	'contain' => ['Departments'],
	'order' => ['username' => 'asc']
]);
$this->set('data', $data);
$this->set('_serialize', array_merge($this->viewVars['_serialize'], ['data']));

Again some notes on this example:

  1. The DataTables component provides a find() function that processes any parameters in the requests initiated by DataTables. It is a drop-in replacement for calling the table's finder directly. There is no need to differentiate between regular and JSON requests in the controller.
  2. We provide a default order argument that needs to match the order argument in the template. Otherwise DataTables will indicate a wrong ordering to the user. This is related to our use of the deferLoading option. If you have any other custom settings for the initial data display, it is typically easier to not use deferLoading.
  3. DataTables performs JSON requests and CakePHP's JSON view uses the _serialize view variable to determine which view variables to send back. The DataTables plugin sets a bunch of these, so it is crucial to append the data variable here. If your view variable is not called 'data', set DataTables ajax.dataSrc option.

Enable dynamic filters and ordering

For columns that are defined as searchable or orderable, DataTables can offer search (i.e. filter) and ordering functionality to the user, respectively. To make this work on the server side, the DataTables component needs to know the column definitions, including the table fields that correspond to columns. We start by defining all columns in the controller (not the view, as before):

$columns = [
	[
		'field' => 'Users.id',
		'data' => 'id',
		'visible' => false,
		'searchable' => false,
	],
	[
		'title' => __('First Name'),
		'field' => 'Users.firstname',
		'data' => 'firstname'
	],
	[
		'title' => __('Last Name'),
		'field' => 'Users.lastname',
		'data' => 'lastname'
	],
	[
		'title' => __('Login name'),
		'field' => 'Users.username',
		'data' => 'username',
		'className' => 'text-primary',
	],
	[
		'title' => __('Department'),
		'field' => 'Departments.name',
		'data' => 'department.name'
	],
];

$data = $this->DataTables->find('Users', 'all', [
	'contain' => ['Departments'],
	'order' => ['username' => 'asc']
], $columns);

$this->set('columns', $columns);
$this->set('data', $data);
$this->set('_serialize', array_merge($this->viewVars['_serialize'], ['data']));

Now, in the view, we use the view variable to pass on the same column definitions to the DataTables helper:

$options = [
	'ajax' => [
		'url' => $this->Url->build() // current controller, action, params
	],
	'data' => $data,
	'deferLoading' => $data->count(), // https://datatables.net/reference/option/deferLoading
	'columns' => $columns,
	'order' => [3, 'asc'], // order by username
];
echo $this->DataTables->table('users-table', $options, ['class' => 'table table-striped']);

A drawback of this method is that styling options find their place in the controller, but would be better suited to stay in the view. You can alternatively extend the column definitions in the view. On the controller side, only the field names and, if applicable, searchable and orderable switches are needed (they are true by default).