PHPackages                             neophp/datatable-package - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. neophp/datatable-package

ActiveLibrary

neophp/datatable-package
========================

Server-side data table with real SQL pagination, sorting, and search for NeoPHP

v0.3.0(yesterday)05↑2900%MITPHPPHP &gt;=8.5CI passing

Since Aug 8Pushed yesterdayCompare

[ Source](https://github.com/NeoPHP-Dev/neo-datatable-package)[ Packagist](https://packagist.org/packages/neophp/datatable-package)[ RSS](/packages/neophp-datatable-package/feed)WikiDiscussions main Synced today

READMEChangelog (5)DependenciesVersions (6)Used By (0)

DataTable Package
=================

[](#datatable-package)

A server-rendered data table for NeoPHP — real SQL pagination, sorting, and search from a single entity class. No AJAX, no JavaScript required to function: every interaction is a plain link or GET form that reloads the page with new query parameters. Reuses NeoPHP core's own `Paginator`and `paginator_links()` — no duplicate pagination logic.

---

Structure
---------

[](#structure)

```
datatable-package/
├── composer.json
├── README.md
└── src/
    ├── NeoDataTablePackage.php
    ├── Service/
    │   ├── DataTableFactory.php
    │   └── DataTableResult.php
    ├── Assets/
    │   └── css/datatable.css
    └── Templates/
        └── components/
            └── DataTable.macro.html.twig

```

---

How it works
------------

[](#how-it-works)

You give `DataTableFactory` an entity class, the columns to display, and the current request's query parameters. It runs a real, paginated, sorted SQL query through NeoPHP's `EntityManager`/`EntityRepository` — nothing is loaded into memory beyond the current page's rows.

```
public function index(Request $request, DataTableFactory $dataTableFactory): Response
{
    $table = $dataTableFactory->createFromEntity(
        AdminUser::class,
        $request->query(),
        columns: [
            ['key' => 'email', 'label' => 'Email', 'sortable' => true],
            ['key' => 'firstName', 'label' => 'First Name', 'sortable' => true],
            ['key' => 'lastName', 'label' => 'Last Name', 'sortable' => true],
        ],
        searchableFields: ['email', 'firstName', 'lastName'],
    );

    return $this->render('pages/admin/users.html.twig', ['table' => $table]);
}
```

```
{% import '@DataTable/components/DataTable.macro.html.twig' as DataTable %}

{{ DataTable.render(table) }}
```

That's the entire integration — one factory call, one macro render.

---

Installation
------------

[](#installation)

```
php bin/neo package:require neophp/datatable-package --project=MyProject
```

Register it in the project's `Config/app.config.php`:

```
return [
    // ...
    'packages' => [
        \Vendor\NeoPHP\DataTablePackage\NeoDataTablePackage::class,
    ],
];
```

No configuration file, no migration.

---

`DataTableFactory::createFromEntity()`
--------------------------------------

[](#datatablefactorycreatefromentity)

ParameterTypePurpose`entityClass``class-string`The entity to query`queryParams``array`Typically `$request->query()` — reads `search`, `sort`, `direction`, `page``columns``list`Which entity fields to display, in order`searchableFields``list`Which fields the search box matches against`perPage``int`Defaults to 20Only fields listed in `columns` are ever read from the entity — the rest of the entity's data is never touched or exposed.

---

No AJAX, by design
------------------

[](#no-ajax-by-design)

Sorting a column, searching, and changing page are all plain links or a GET form — clicking a column header reloads the page with `?sort=email&direction=desc` in the URL. This keeps the package simple and avoids exposing a generic query endpoint that would need careful security review (allowed entities, allowed fields, injection risks).

If you need a fully dynamic, no-reload experience, this package is not built for that — you would need to build your own AJAX layer around `DataTableFactory`, which still only ever runs the query you configured (no arbitrary entity/field can be requested from the client).

---

Theming
-------

[](#theming)

Every visual value is a CSS custom property scoped to `.dt-wrapper` — override them on an ancestor element to match your project:

```
.dt-wrapper {
    --dt-accent: #6366f1;
    --dt-bg: #161923;
    --dt-bg-alt: #1b2030;
    --dt-border: #2d3342;
    --dt-text: #e5e7eb;
    --dt-text-muted: #9ca3af;
}
```

You can also skip `datatable.css` entirely and style the `.dt-*` class names yourself for full control.

---

Known limitations
-----------------

[](#known-limitations)

- **Search implementation depends on `EntityRepository`'s own capabilities.** This package does not build raw SQL itself — it relies entirely on `EntityRepository::findBy()`/`count()`. If your version of `EntityRepository` does not support a `LIKE`-style multi-field search through its criteria array, the `search` feature may not filter results as expected. Sorting and pagination do not have this limitation, as they rely on `orderBy`/`limit`/`offset`, which are standard `findBy()` parameters.
- **No column formatting.** A column's raw entity field value is displayed as-is (`{{ row[column.key] }}`) — dates, booleans, and relations are not automatically formatted. Format them yourself before passing rows, or extend the macro in your own project.
- **No relation/joined columns.** Only scalar fields directly on the queried entity can be displayed — no dot-notation for related entity fields (e.g. `role.name`).

---

License
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 68.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

5

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/212323688?v=4)[BBoezio](/maintainers/BenjiLeLoustik)[@BenjiLeLoustik](https://github.com/BenjiLeLoustik)

---

Top Contributors

[![BenjiLeLoustik](https://avatars.githubusercontent.com/u/212323688?v=4)](https://github.com/BenjiLeLoustik "BenjiLeLoustik (11 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (5 commits)")

### Embed Badge

![Health badge](/badges/neophp-datatable-package/health.svg)

```
[![Health](https://phpackages.com/badges/neophp-datatable-package/health.svg)](https://phpackages.com/packages/neophp-datatable-package)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
