DataTable Pattern
Lacelium champions a "Server-Side HTML First" architecture for reactive data tables, combining robust database filtering, multi-column sorting, and pagination with seamless asynchronous SPA transitions.
HTML-First Reactive Architecture
Instead of heavy client-side JavaScript tables (which download megabytes of raw JSON, risk memory leaks, and break search engine indexing), Lacelium performs querying, slicing, and rendering server-side. Sorting and filtering occur via standard query parameters (?sort=-priority,created_at&status=active) swapped into the DOM seamlessly via AJAX.
Interactive Sandbox Table
Click table column headers to test interactive sorting with visual ascending and descending indicators:
| ID ↕ | Task Name ↕ | Status ↕ |
|---|---|---|
#1024 |
Async Job Pipeline | Active |
#1025 |
Redis Session Cache | Pending |
#1026 |
ArgusKit Studio Sync | Syncing |
#1027 |
Database Read-Replica | Active |
Controller Implementation
Controllers parse incoming sorting keys and apply them to the query builder before passing the builder to the paginator:
namespace App\Http\Controllers;
use App\Models\Task;
use Lacelium\Core\Http\Request;
use Lacelium\Core\Http\Response;
use Lacelium\Core\Pagination\Paginator;
class TaskTableController
{
public function index(Request $request, Response $response): Response
{
$query = Task::query()->where('is_active', true);
// 1. Multi-Column Sorting: ?sort=-priority,title
if ($sort = $request->query('sort')) {
foreach (explode(',', $sort) as $column) {
$direction = str_starts_with($column, '-') ? 'desc' : 'asc';
$field = ltrim($column, '-');
$query->orderBy($field, $direction);
}
} else {
$query->orderByDesc('id');
}
// 2. Query Filtering
if ($status = $request->query('status')) {
$query->where('status', $status);
}
// 3. Paginate
$paginator = Paginator::fromQuery($query, $request, perPage: 15);
$tasks = (clone $query)->forPage($paginator->currentPage, 15)->get();
return $response->render('docs/tasks/table.twig', [
'tasks' => $tasks,
'paginator' => $paginator,
]);
}
}
Twig Template Integration
Use data attributes on column header links. Lacelium automatically binds click handlers to update the URL and trigger SPA fragment swaps:
Title
{% if current_sort == 'title' %}↑{% elseif current_sort == '-title' %}↓{% endif %}
Priority
{% if current_sort == 'priority' %}↑{% elseif current_sort == '-priority' %}↓{% endif %}
{% for task in tasks %}
{{ task.title }}
{{ task.priority }}
{% endfor %}
{% include 'docs/paginator.twig' with { paginator: paginator } %}
Multi-Column Shift + Click Interaction
Lacelium's table script natively supports composite sorting keys:
| User Action | Query String Result | Behavior |
|---|---|---|
| Standard Click | ?sort=priority |
Clears previous sort rules and sorts exclusively by the clicked column. |
| Shift + Click | ?sort=priority,-title |
Appends or toggles the column in the multi-column composite sorting chain. |
Performance Best Practices
- Compound SQL Indexes: When supporting multi-column sorting (e.g.
status + created_at), create corresponding compound database indexes. - Allowed Sort Whitelisting: Always validate sort column names against an allowed whitelist in your controller to prevent SQL injection or arbitrary column disclosure.
- Query Cloning: Always execute
clone $querybefore calling->forPage()to keep the paginator instance unmutated.
Next Steps
- Configure pagination controls and sort queries in Pagination Guide.
- Trigger row actions and edit drawers via Modal System.
- Optimize queries and indexing using Database & Eloquent.