Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Windows Tipps & SecurityGetting Repeated No Caller ID Calls? Here’s What’s Really Going On(22.09.2026 um 22:31 Uhr)
Windows Tipps & SecurityHöllenmaschine: Gaming-Peripherie für gut 1.800 Euro für die HMX 6(23.09.2026 um 10:20 Uhr)
Windows Tipps & SecurityDas nächste große Ding: KI-Agenten(23.09.2026 um 10:30 Uhr)
Sichere ProgrammierungHow AI Is Making Restaurant Menus Easier to Navigate(23.09.2026 um 10:55 Uhr)
Windows Tipps & SecurityGetting Repeated No Caller ID Calls? Here’s What’s Really Going On(22.09.2026 um 22:31 Uhr)
Windows Tipps & SecurityHöllenmaschine: Gaming-Peripherie für gut 1.800 Euro für die HMX 6(23.09.2026 um 10:20 Uhr)
Windows Tipps & SecurityDas nächste große Ding: KI-Agenten(23.09.2026 um 10:30 Uhr)
Sichere ProgrammierungHow AI Is Making Restaurant Menus Easier to Navigate(23.09.2026 um 10:55 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

UX DataTables in 2026: typed columns, server-side processing, API Platform, Mercure and inline editing

A while ago I wrote a first post introducing UX DataTables, a Symfony bundle that integrates the DataTables.net library into Symfony applications. Since then the bundle has changed a lot. The builder-service approach from the original…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

A while ago I wrote a first post introducing UX DataTables, a Symfony bundle that integrates the DataTables.net library into Symfony applications.



Since then the bundle has changed a lot. The builder-service approach from the original post is gone; the bundle now revolves around a single base class you extend, with strongly-typed columns, server-side processing handled for you, row actions, an inline edit modal, and first-class integrations with API Platform and Mercure.



This post is a tour of where the bundle stands today.






Installation






composer require pentiminax/ux-datatables






Requirements:




  • PHP 8.3+

  • Symfony 7.0 / 8.0

  • Symfony StimulusBundle (shipped with Symfony UX)



The frontend ships as the @pentiminax/ux-datatables ES module. DataTables.net and its extensions are lazy-loaded by the Stimulus controller, so you only download the JavaScript you actually use.






The new mental model: one class per table



Instead of building a table inline in your controller, you now describe a table once in a dedicated class extending AbstractDataTable, decorated with the #[AsDataTable] attribute pointing at your entity:




use App\Entity\User;
use Pentiminax\UX\DataTables\Attribute\AsDataTable;
use Pentiminax\UX\DataTables\Column\BooleanColumn;
use Pentiminax\UX\DataTables\Column\DateColumn;
use Pentiminax\UX\DataTables\Column\NumberColumn;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Model\AbstractDataTable;

#[AsDataTable(User::class)]
final class UserDataTable extends AbstractDataTable
{
public function configureColumns(): iterable
{
return [
NumberColumn::new('id', 'ID'),
TextColumn::new('firstName', 'First name'),
TextColumn::new('email', 'Email'),
BooleanColumn::new('active', 'Active'),
DateColumn::new('createdAt', 'Created at'),
];
}
}






Wire it into a controller. In most cases your controller only needs to render the page — the bundle ships its own AJAX controller and wires the data endpoint for you:




#[Route('/users', name: 'app_users')]
public function index(UserDataTable $table): Response
{
return $this->render('user/index.html.twig', [
'table' => $table,
]);
}






And render it in Twig:




{{ render_datatable(table) }}






That is the whole loop. The bundle registers a built-in controller behind the /datatables/ajax/data route (no route import needed — it's loaded automatically). For server-side tables, the AJAX URL is resolved automatically: each AbstractDataTable is addressed by a signed HMAC token, so the frontend calls the shared endpoint, the bundle looks the table up in its registry, runs the query and returns the JSON. You don't write a data endpoint, and you don't expose your table classes by name in the URL.



If you ever need the table's data to flow through your own route (a custom endpoint, extra auth, a non-standard payload), the same class can still do it manually:




#[Route('/users', name: 'app_users')]
public function index(UserDataTable $table, Request $request): Response
{
$table->handleRequest($request);

if ($table->isRequestHandled()) {
return $table->getResponse(); // JSON for a DataTables AJAX call
}

return $this->render('user/index.html.twig', ['table' => $table]);
}






handleRequest() detects whether the incoming request is a DataTables AJAX call; if it is, getResponse() returns the JSON payload, otherwise you render the page.






Strongly-typed columns



Columns are PHP objects, created with a ::new() factory and configured fluently. There are dedicated types for the common cases instead of stringly-typed configuration:
























































Column Use for
TextColumn Plain text or HTML
NumberColumn Integers, floats
MoneyColumn Monetary amounts (currency, cents)
DateColumn Dates / datetimes
BooleanColumn True/false, optionally an AJAX toggle switch
ChoiceColumn Finite value sets (statuses, enums)
EmailColumn Clickable mailto: links
ImageColumn Image URLs rendered as thumbnails
UrlColumn Links from raw URLs, routes, or callables
TemplateColumn Custom server-side Twig rendering
ActionColumn Row action buttons


Each variant exposes intent-revealing methods:




TextColumn::new('content')->html()->utf8();
NumberColumn::new('price')->formatted();
MoneyColumn::new('price')->currency('EUR')->storedAsCents();
BooleanColumn::new('active')->renderAsSwitch();






All columns inherit the common knobs from AbstractColumnsetVisible(), setWidth(), setOrderable(), setSearchable(), disableGlobalSearch(), setExportable(), setDefaultContent(), and more. Column titles can be Symfony translation keys; they are resolved automatically.



For computed columns backed by a SELECT alias rather than a mapped field, setOrderExpression() lets you provide the raw DQL used in the ORDER BY, so sorting works on derived values too:




NumberColumn::new('invoiceCount', 'Invoices')
->setOrderExpression('invoiceCount') // SELECT alias added in customizeQueryBuilder()
->setSearchable(false);









Server-side processing, handled for you



For large datasets you flip a single switch and the bundle takes care of paging, ordering, global search and per-column search against Doctrine:




public function configureDataTable(DataTable $table): DataTable
{
return $table
->serverSide(true)
->pageLength(25);
}






Under the hood a chain-of-responsibility query pipeline applies global search, per-column search and ordering, using a set of search strategies (contains, starts-with, equals, greater-than, in-list, …). Need to constrain the result set? Override customizeQueryBuilder():




protected function customizeQueryBuilder(QueryBuilder $qb, DataTableRequest $request): QueryBuilder
{
return $qb
->andWhere('e.active = :active')
->setParameter('active', true);
}






If you need to enrich a whole page without an N+1 (load metrics, project to DTOs), override projectPage(): it receives the already-paginated page of entities and returns a parallel list, so columns read the projection while actions still receive the source entity.



Client-side mode is just as easy — provide inline data and DataTables does paging/search in the browser. The bundle hydrates the rows for you through the same column pipeline.






Row actions and per-row permissions



Row buttons are declared with configureActions(). Edit, detail and delete are first-class, and you can point them at a URL or a route resolver:




public function configureActions(Actions $actions): Actions
{
return $actions
->add(Action::detail('View')->setIcon('bi bi-eye')
->linkToUrl(fn (User $u) => $this->router->generate('user_show', ['id' => $u->getId()])))
->add(Action::edit('Edit')->setIcon('bi bi-pencil'))
->add(Action::delete('Delete')->askConfirmation('Delete this user?'));
}






Actions integrate with Symfony Security. A static attribute is checked once before rendering; a per-row check receives the row and decides visibility individually:




Action::delete()
->permission('ROLE_ADMIN'); // evaluated once

Action::edit()
->permission('EDIT', fn (User $u) => $u); // voter evaluated per row






The same permission() mechanism is available on columns, so you can hide a whole column from users who shouldn't see it.






Inline editing with an auto-generated form



Action::edit() opens a Bootstrap 5 modal containing a Symfony Form generated from your columns. No form class to write:




  1. The user clicks Edit.

  2. The controller fetches a pre-filled form from GET /datatables/ajax/edit-form.

  3. On submit, POST /datatables/ajax/edit-form validates and persists.

  4. On error, only the modal body is re-rendered in place; on success the table reloads automatically.



Column types map to form types automatically (BooleanColumnCheckboxType, ChoiceColumnChoiceType, DateColumnDateType, …). Identifier columns are rendered disabled, and hideWhenUpdating() removes a column from the form entirely.



Both the Twig template and the JavaScript modal adapter are overridable — globally, via the attribute, or per table — so you can swap Bootstrap for a native <dialog> or any UI kit.



This requires symfony/form (and symfony/twig-bundle to render the modal).






API Platform integration



If your backend already exposes Hydra collections through API Platform, the bundle can drive a table straight off them. It is opt-in — add apiPlatform: true:




#[AsDataTable(entityClass: Book::class, apiPlatform: true)]
final class BookDataTable extends AbstractDataTable
{
}






With that enabled, the bundle:





  • auto-detects columns from API Platform property metadata (respecting serialization groups),


  • resolves the collection URL and wires AJAX automatically,

  • activates a Hydra adapter on the frontend that translates DataTables query params into API Platform params (page, itemsPerPage, order[field], q, filters) and maps hydra:member / hydra:totalItems back into the DataTables response shape.



The global search box is mapped to the q parameter, so wiring a free-text search is just a QueryParameter on your resource:




#[ApiResource(operations: [
new GetCollection(parameters: [
'q' => new QueryParameter(
filter: new OrFilter(new FreeTextQueryFilter(new PartialSearchFilter())),
properties: ['email', 'firstName', 'lastName'],
),
]),
])]
final class User {}









Real-time refresh with Mercure



Tables can refresh themselves when the underlying data changes elsewhere. Enable Mercure on the attribute:




#[AsDataTable(Book::class, mercure: true)]
final class BookDataTable extends AbstractDataTable
{
}






The Stimulus controller opens an EventSource to your Mercure hub and calls ajax.reload() on each message — no full page reload, current paging preserved. Topics can be auto-resolved (from API Platform metadata or an IRI template) or declared explicitly, and you can tune withCredentials and a client-side debounceMs to protect the backend under bursty updates:




$dataTable->mercure(
topics: ['admin/books', 'admin/authors'],
withCredentials: true,
debounceMs: 300,
);






When symfony/mercure is installed, the bundle also registers a MercureUpdatePublisher and broadcasts after inline edits, so an edit in one browser refreshes every open table.






Scaffolding with make:datatable



You rarely start from a blank file. The MakerBundle command reads a Doctrine entity and generates the table class for you, mapping field types to the right column (int/floatNumberColumn, boolBooleanColumn, DateTimeInterfaceDateColumn):




php bin/console make:datatable User






It produces a ready-to-edit class in src/DataTables/ with configureColumns() and a mapRow() skeleton.






Extensions



The DataTables.net extensions are exposed through PHP helpers and lazy-loaded frontend adapters, configured in configureExtensions():












































Extension Use for
Buttons CSV / Excel / PDF / print export
Select Row / column / cell selection
Column Control Per-column order and search controls
Responsive Mobile-friendly collapsing
KeyTable Keyboard navigation
Scroller Virtualized rendering for huge tables
Fixed Columns Pin edge columns while scrolling
ColReorder User-driven column reordering





public function configureExtensions(DataTableExtensions $extensions): DataTableExtensions
{
return $extensions->addExtension(new ButtonsExtension([
ButtonType::CSV,
ButtonType::EXCEL,
ButtonType::PDF,
]));
}









Wrapping up



UX DataTables has grown from "render a DataTable from PHP" into a complete, typed layer over DataTables.net:




  • one AbstractDataTable class per table, with the #[AsDataTable] attribute,

  • a dozen strongly-typed column types,

  • server-side processing, custom query builders and page projection,

  • row actions with Symfony Security checks,

  • inline editing via auto-generated Symfony Forms in an overridable modal,

  • API Platform and Mercure integrations,


  • make:datatable scaffolding and the full set of DataTables.net extensions.



The full documentation lives at pentiminax.github.io/ux-datatables, and the source is on GitHub.



Feedback and contributions are very welcome.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten UX DataTables in 2026: typed columns, server-side processing, API Platform, Mercure and inline editing

Thematisch verwandte Begriffe: DataTables, 2026, typed, columns · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-96258 | A vulnerability has been found in onSite internet GmbH Auktion NG Auktio…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick