Element - Data Table
The CElement_Component_DataTable is a powerful component for displaying tabular data with built-in support for pagination, sorting, searching, AJAX loading, row actions, and export.
Add a data table to CApp using addTable():
$app = c::app();
$table = $app->addTable();
$table->setDataFromModel(UserModel::class);
$table->addColumn('name')->setLabel('Name');
$table->addColumn('email')->setLabel('Email');
return $app;
Data Sources
From Model (Recommended)
Load data from an Eloquent-style model with optional query customization:
$table->setDataFromModel(UserModel::class, function (CModel_Query $q) {
$q->where('status', 'active');
$q->with(['role']);
$q->orderBy('created', 'desc');
});
$table->addColumn('name')->setLabel('Name');
$table->addColumn('role.name')->setLabel('Role');
Dot notation (role.name) accesses related model attributes. Use $q->with() for eager loading to avoid N+1 queries.
From Model Query
Pass a pre-built query instance:
$query = UserModel::where('status', 'active')->orderBy('name');
$table->setDataFromModelQuery($query);
From SQL Query
Load data directly from a raw SQL query:
$q = 'SELECT u.*, r.name AS role_name
FROM users AS u
INNER JOIN roles AS r ON r.role_id = u.role_id
WHERE u.status > 0';
$table->setDataFromQuery($q);
$table->addColumn('role_name')->setLabel('Role');
$table->addColumn('username')->setLabel('Username');
From Array
Load data from a PHP array (not recommended for large datasets):
$table->setDataFromArray([
['role' => 'Developer', 'username' => 'albert', 'name' => 'Albert'],
['role' => 'QA', 'username' => 'adam', 'name' => 'Adam'],
]);
$table->addColumn('role')->setLabel('Role');
$table->addColumn('username')->setLabel('Username');
$table->addColumn('name')->setLabel('Name');
From Collection
Load data from a CCollection:
$collection = UserModel::all();
$table->setDataFromCollection($collection);
From Callback
Load data using a custom callback:
$table->setDataFromCallback(function () {
return UserModel::where('active', true)->get();
});
From Closure
$table->setDataFromClosure(function () {
return CDatabase::instance()->query('SELECT * FROM users');
});
The default database connection is determined by the default key in config/database.php. Use setDomain() or setDatabase() to change it.
Columns
Add columns using addColumn(). The argument is the field name (database column or model attribute):
$table->addColumn('name')->setLabel('Name');
$table->addColumn('email')->setLabel('Email');
$table->addColumn('created')->setLabel('Registered');
Column Options
| Method | Description |
|---|---|
setLabel($label) |
Column header text |
setWidth($width) |
Column width (e.g. '200px', '20%') |
setAlign($align) |
Text alignment: 'left', 'center', 'right' |
setAlignCenter() |
Shorthand for center alignment |
setAlignRight() |
Shorthand for right alignment |
setSortable($bool) |
Enable/disable sorting on this column |
setSearchable($bool) |
Enable/disable searching on this column |
setVisible($bool) |
Show/hide column |
setInvisible() |
Hide the column |
setNoLineBreak($bool) |
Prevent text wrapping |
setNoWrap($bool) |
Prevent text wrapping |
setDataType($type) |
Column data type |
setFormat($format) |
Display format |
addClass($class) |
Add CSS class to column cells |
Column Callback
Transform the displayed value using a callback. The callback receives the full row data and the column value:
$table->addColumn('status')->setLabel('Status')->setCallback(function ($row, $value) {
if ($value == 'active') {
return '<span class="badge badge-success">Active</span>';
}
return '<span class="badge badge-danger">Inactive</span>';
});
$table->addColumn('amount')->setLabel('Amount')->setCallback(function ($row, $value) {
return 'Rp ' . number_format($value, 0, ',', '.');
});
$table->addColumn('request')->setLabel('Request')->setCallback(function ($row, $value) {
return CElement_Component_ShowMore::factory()
->addClass('whitespace-pre')
->add(json_encode(json_decode($value, true), JSON_PRETTY_PRINT));
});
Search and Sort Callbacks
Customize how searching and sorting behave for a column:
$table->addColumn('full_name')->setLabel('Name')
->setSearchCallback(function ($query, $search) {
$query->where(function ($q) use ($search) {
$q->where('first_name', 'like', "%{$search}%")
->orWhere('last_name', 'like', "%{$search}%");
});
})
->setSortCallback(function ($query, $direction) {
$query->orderBy('first_name', $direction);
});
Export Callback
Customize the value when exporting to Excel:
$table->addColumn('status')->setLabel('Status')
->setCallback(function ($row, $value) {
return '<span class="badge">' . $value . '</span>';
})
->setExportCallback(function ($row, $value) {
return $value;
});
Row Actions
Add action buttons to each row (edit, delete, view, etc.):
$table->addRowAction()
->setIcon('ti ti-pencil')
->setLabel('Edit')
->setLink(c::url('admin/user/edit/{user_id}'));
$table->addRowAction()
->setIcon('ti ti-trash')
->setLabel('Delete')
->setConfirm(true)
->setLink(c::url('admin/user/delete/{user_id}'));
Use {column_name} placeholders in the URL — they are replaced with the row's column value.
Dynamic Row Actions
Use withRowCallback to change the action based on row data:
$table->addRowAction()->withRowCallback(function ($element, $row) {
$isActive = carr::get($row, 'is_active');
$element->setIcon($isActive ? 'ti ti-close' : 'ti ti-check')
->setLabel($isActive ? 'Deactivate' : 'Activate')
->setLink(c::url('admin/user/toggle/{user_id}'));
});
Conditional Row Actions
Show or hide actions based on conditions:
$table->addRowAction()->withRowCallback(function ($element, $row) {
$isLocked = carr::get($row, 'is_locked');
if ($isLocked) {
$element->setVisibility(false);
} else {
$element->setIcon('ti ti-trash')
->setLabel('Delete')
->setConfirm(true)
->setLink(c::url('admin/user/delete/{user_id}'));
}
});
Action Location
Control where the action column appears:
$table->setActionLocation('right'); // default
$table->setActionLocation('left');
Action Header Label
$table->setActionHeaderLabel('Actions');
Table Options
Display and Pagination
| Method | Description |
|---|---|
setDisplayLength($n) |
Rows per page (default: 10) |
setPagingList([10, 25, 50, 100]) |
Page size options in dropdown |
setLabelNoData($label) |
Text shown when table is empty |
setInfoText($text) |
Custom info text below the table |
$table->setDisplayLength(25);
$table->setPagingList([10, 25, 50, 100]);
$table->setLabelNoData('No records found');
AJAX Loading
$table->setAjax(true); // Enable AJAX data loading
$table->setAjaxMethod('POST'); // HTTP method for AJAX requests (default: GET)
Layout and Appearance
| Method | Description |
|---|---|
setTableStriped($bool) |
Striped row styling |
setTableBordered($bool) |
Add borders |
setResponsive($bool) |
Enable responsive layout |
setScrollX($bool) |
Enable horizontal scrolling |
setScrollY($bool) |
Enable vertical scrolling |
setShowHeader($bool) |
Show/hide table header |
setHeaderNoLineBreak($bool) |
Prevent header text wrapping |
setWidgetTitle($bool) |
Show title in widget wrapper |
setHeaderSortable($bool) |
Enable/disable column reordering by drag |
Advanced Options
| Method | Description |
|---|---|
setDom($dom) |
Custom DataTables DOM layout string |
setColReorder($bool) |
Enable column reordering |
setFixedColumn($n) |
Fix the first N columns |
setFixedHeader($bool) |
Fix the header when scrolling |
setNumbering($bool) |
Show row numbers |
setGroupBy($column) |
Group rows by column value |
setKey($fieldname) |
Set the primary key field |
setAutoRefresh($seconds) |
Auto-refresh data every N seconds |
setCustomColumnHeader($html) |
Custom HTML for column headers |
setOption($key, $val) |
Set any DataTables option directly |
Database Connection
$table->setDomain('other-domain.com'); // Use a different domain's config
$table->setDatabase($dbInstance); // Use a specific database instance
$table->setDatabaseResolver($resolverCallback); // Custom database resolver
Row Selection
Enable row selection checkboxes (for bulk operations):
$table->haveRowSelection();
Row Class Callback
Dynamically add CSS classes to rows:
$table->setRowClassCallback(function ($row) {
if (carr::get($row, 'status') === 'overdue') {
return 'table-danger';
}
return '';
});
Export
Export the table data to Excel:
$table->downloadExcel('users.xlsx');
Or queue the export for large datasets:
$table->queueDownloadExcel('exports/users.xlsx', 'local');
Translations
Table UI labels (pagination, search, etc.) can be customized by overriding the translation file. Copy from system/i18n/en_US/element/datatable.php to your application's i18n/ directory.
<?php
return [
'emptyTable' => 'No data available in table',
'info' => 'Showing _START_ to _END_ of _TOTAL_ entries',
'infoEmpty' => 'Showing 0 to 0 of 0 entries',
'infoFiltered' => '(filtered from _MAX_ total entries)',
'lengthMenu' => 'Show _MENU_ entries',
'loadingRecords' => 'Loading...',
'processing' => 'Processing...',
'search' => 'Search',
'zeroRecords' => 'No matching records found',
'paginate' => [
'first' => 'First',
'last' => 'Last',
'next' => 'Next',
'previous' => 'Previous',
],
];
See the DataTables language documentation for all available keys.