italix/datasets

Server-side data tables and trees: column and action definitions in PHP, driver-rendered client configuration, whitelisted column names

Maintainers

Package info

github.com/italix-net/datasets

pkg:composer/italix/datasets

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.1.1 2026-08-29 22:16 UTC

This package is not auto-updated.

Last update: 2026-08-30 19:42:42 UTC


README

PHP Version License

Pluggable datatable and data-tree rendering for PHP. Works with any TableMeta-compatible source and multiple JS datatable libraries.

Features

  • Server-side pagination, sorting, searching, and filtering
  • Multi-column sorting with Shift+click interactive support
  • Per-row action buttons (edit, delete, view) with confirmation dialogs
  • Toolbar with bulk actions on none, selected, or all rows
  • Row selection via checkboxes or row highlighting
  • Global search with debounce and minimum length
  • Column-level header filters (text input, select dropdown)
  • Column formatting, alignment, freezing, and visibility control
  • Hierarchical data trees with configurable parent-child relationships
  • Event system mapping named events to JS callback functions
  • Pluggable driver architecture for different JS datatable libraries
  • Tabulator JS as the first built-in driver
  • PHP 7.4+ compatible, PSR-4 autoloaded
  • Fluent, chainable API with snake_case conventions

Installation

composer require italix/datasets

Requirements

Quick Start

<?php

use Italix\DataSets\DataSet;
use Italix\DataSets\Drivers\Tabulator\TabulatorDriver;

// Create a dataset from any TableMeta source (e.g. from italix/orm)
$ds = new DataSet($usersTable);

// Select and configure columns
$ds->columns(['name', 'email', 'role', 'created_at']);

$ds->column('name')->sortable(true)->searchable(true)->width('200px');
$ds->column('email')->sortable(true)->searchable(true);
$ds->column('role')->sortable(true)->horizontal_align('center');
$ds->column('created_at')->sortable(true)->formatter('datetime');

// Server-side data loading
$ds->ajax_url('/api/users')
    ->per_page(25)
    ->default_sort('created_at', 'desc');

// Render with Tabulator
$driver = new TabulatorDriver();
$script = $driver->render_script($ds, '#users-table', 'usersTable');
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
<script src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>

<div id="users-table"></div>
<script><?= $script ?></script>

Action Buttons

Add per-row action buttons with optional confirmation dialogs:

$ds->action_column()
    ->width('150px')
    ->frozen(true);

$ds->action_column()->button('edit', 'Edit')
    ->css_class('btn btn-sm btn-primary')
    ->icon('fa fa-pencil');

$ds->action_column()->button('delete', 'Delete')
    ->css_class('btn btn-sm btn-danger')
    ->icon('fa fa-trash')
    ->confirm('Are you sure?');

// Map actions to JS callback functions
$ds->on('edit', 'onEditUser');
$ds->on('delete', 'onDeleteUser');
function onEditUser(rowData, row) {
    window.location.href = '/users/' + rowData.id + '/edit';
}

function onDeleteUser(rowData, row) {
    fetch('/api/users/' + rowData.id, { method: 'DELETE' })
        .then(function() { row.delete(); });
}

Toolbar & Bulk Actions

Add toolbar buttons that operate on no data, selected rows, or all rows:

$ds->selectable(true);

$ds->toolbar()->position('top');

// scope='none' — callback receives no arguments
$ds->toolbar()->button('add', 'New User', 'none')
    ->css_class('btn btn-success')
    ->icon('fa fa-plus');

// scope='selected' — callback receives selected row data
$ds->toolbar()->button('delete_selected', 'Delete Selected', 'selected')
    ->css_class('btn btn-danger')
    ->confirm('Delete selected users?');

// scope='all' — callback receives all row data
$ds->toolbar()->button('export', 'Export', 'all')
    ->css_class('btn btn-outline-secondary');

$ds->on('add', 'onAddUser');
$ds->on('delete_selected', 'onDeleteSelected');
$ds->on('export', 'onExportUsers');

Row Selection

Two selection modes:

// Checkbox mode — adds a checkbox column, supports select-all
$ds->selectable(true);

// Highlight mode — click rows to select, Ctrl+click for multi-select
$ds->selectable('highlight');

Search & Filtering

// Global search input above the table
$ds->global_search(true, 'Search users...')
    ->search_debounce(400)
    ->search_min_length(2);

// Mark columns as searchable (sent as search_columns[] in AJAX requests)
$ds->column('name')->searchable(true);
$ds->column('email')->searchable(true);

// Column-level header filters
$ds->column('role')->header_filter('select');
$ds->column('name')->header_filter('input');

Multi-column Sorting

// Call default_sort() multiple times to add sort levels
$ds->default_sort('status', 'asc');
$ds->default_sort('created_at', 'desc');

// Users can also Shift+click column headers interactively

Row Events

$ds->on('row_click', 'onRowClick');
$ds->on('row_dbl_click', 'onRowDblClick');
$ds->on('row_context', 'onRowContext');
$ds->on('row_selected', 'onRowSelected');
$ds->on('row_deselected', 'onRowDeselected');
// Each callback receives (rowData, row, event)
function onRowDblClick(rowData, row, event) {
    window.location.href = '/users/' + rowData.id + '/edit';
}

Data Trees

Display hierarchical data with parent-child relationships:

use Italix\DataSets\DataTree;

$tree = new DataTree($categoriesTable);
$tree->columns(['name', 'sort_order']);
$tree->ajax_url('/api/categories');

$tree->tree_config()
    ->parent_column('parent_id')
    ->id_column('id')
    ->start_open(true)
    ->toggle_column('name');

Server-Side Handler

Handle AJAX requests on the server with ServerSideRequest and ServerSideResponse:

use Italix\DataSets\ServerSideRequest;
use Italix\DataSets\ServerSideResponse;

$request = ServerSideRequest::from_globals();

// Pagination
$page     = $request->page();       // 1-based
$per_page = $request->per_page();   // default 25
$offset   = $request->offset();     // calculated

// Sorting (single or multi-column)
$sort_col = $request->sort_column();
$sort_dir = $request->sort_direction();
$sorts    = $request->sorts();  // [['column' => '...', 'direction' => '...'], ...]

// Search
$search   = $request->search();
$columns  = $request->search_columns();

// Column filters
$filters  = $request->filters();  // ['role' => 'admin', ...]

// Build and send the response
$response = ServerSideResponse::from_request($rows, $total, $request);
$response->send();  // outputs JSON and exits

The response format:

{
    "data": [{"id": 1, "name": "..."}],
    "total": 150,
    "page": 1,
    "per_page": 25,
    "last_page": 6
}

Column Configuration

$ds->column('price')
    ->label('Unit Price')              // Display label
    ->sortable(true)                   // Enable sorting
    ->searchable(true)                 // Include in global search
    ->visible(false)                   // Hidden (toggle-able by user)
    ->width('120px')                   // Fixed width
    ->min_width('80px')                // Minimum width
    ->formatter('money', ['symbol' => ''])  // Tabulator formatter
    ->horizontal_align('right')                 // Cell alignment
    ->header_align('right')            // Header alignment
    ->frozen(true)                     // Sticky column
    ->css_class('font-mono')           // CSS class
    ->header_filter('input')           // Column header filter
    ->extra(['editor' => 'input']);     // Driver-specific options

Driver System

DataSets uses a pluggable driver system. Register and switch drivers:

use Italix\DataSets\Drivers\DriverRegistry;
use Italix\DataSets\Drivers\Tabulator\TabulatorDriver;

$registry = new DriverRegistry();
$registry->register(new TabulatorDriver());

$driver = $registry->get('tabulator');
$config = $ds->render($driver);    // array
$json   = $ds->to_json($driver);   // JSON string
$script = $driver->render_script($ds, '#table');  // JS snippet

Implement DriverInterface to create custom drivers for other JS libraries.

Helper Functions

use function Italix\DataSets\dataset;
use function Italix\DataSets\data_tree;

$ds   = dataset($usersTable)->columns(['name', 'email'])->ajax_url('/api/users');
$tree = data_tree($categoriesTable)->ajax_url('/api/categories');

Using with Italix ORM

DataSets works with any TableMeta-compatible source. With Italix ORM, your table schemas are already TableMeta:

use function Italix\Orm\Schema\{mysql_table, integer, varchar, timestamp};

$users = mysql_table('users', [
    'id'         => integer()->primary_key()->auto_increment(),
    'name'       => varchar(100)->not_null(),
    'email'      => varchar(255)->not_null(),
    'created_at' => timestamp()->default_value('CURRENT_TIMESTAMP'),
]);

// Pass directly to DataSet — no adapters needed
$ds = new DataSet($users);

Examples

See the examples/ directory for complete, runnable examples:

Example Description
01-basic-table.php Basic table with columns, sorting, pagination, AJAX
02-column-formatting.php Formatters, alignment, frozen columns, visibility
03-action-buttons.php Per-row Edit/View/Delete buttons with confirm dialogs
04-toolbar-bulk-actions.php Toolbar with none/selected/all scope buttons
05-search-and-filtering.php Global search and column header filters
06-multi-sort.php Multi-column default sorting
07-row-selection.php Checkbox vs highlight selection modes
08-row-events.php Row click, double-click, context menu events
09-data-tree.php Hierarchical data tree with parent-child config
10-server-side-handler.php ServerSideRequest/Response with query building
11-complete-crud.php Full CRUD table combining all features
12-driver-registry.php Driver registry and custom driver pattern
13-helper-functions.php dataset() and data_tree() shorthand functions

License

MPL-2.0. See LICENSE for details.