Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Data Grid Bundle Laravel Package

vysokeskoly/data-grid-bundle

A PHP data grid bundle for building configurable, sortable, and filterable tables with pagination, reusable column definitions, and clean integration into your application's listings and admin views.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a fork of the Kitpages DataGridBundle, so the Composer name is vysokeskoly/data-grid-bundle but the PHP namespace and service IDs remain Kitpages\DataGridBundle / kitpages_data_grid.*. Expect that mismatch from the first minute.

Minimal install and wiring:

composer require vysokeskoly/data-grid-bundle

Register the bundle (Symfony 4/5 config/bundles.php):

Kitpages\DataGridBundle\KitpagesDataGridBundle::class => ['all' => true],

There is no mandatory configuration for a first grid — the bundle auto-wires a grid manager and a Twig extension. Confirm both are present before writing code:

bin/console debug:container kitpages_data_grid
bin/console debug:twig | grep -i grid

Where to look first, in order:

  1. Grid/GridConfig.php — the fluent object that defines columns, sort, filters, pagination. This is the API you will spend 90% of your time in.
  2. Datasource/DoctrineORM/DatasourceDoctrineORM.php — wraps a Doctrine QueryBuilder.
  3. Grid/GridManager.php — turns a GridConfig into a renderable Grid.
  4. The Twig extension / Resources/views — the default table markup you will override.

First use case — a paginated, sortable, filterable admin listing:

use Kitpages\DataGridBundle\Datasource\DoctrineORM\DatasourceDoctrineORM;
use Kitpages\DataGridBundle\Grid\GridConfig;
use Kitpages\DataGridBundle\Grid\GridManager;

public function list(GridManager $gridManager, EntityManagerInterface $em)
{
    $qb = $em->getRepository(Post::class)->createQueryBuilder('p');

    $datasource = new DatasourceDoctrineORM($qb);
    $datasource->setCountFieldName('p.id');

    $gridConfig = (new GridConfig())
        ->setDatasource($datasource)
        ->setCountFieldName('p.id')
        ->setItemsPerPage(25)
        ->addField('p.id',    ['label' => 'ID',    'sortable' => true])
        ->addField('p.title', ['label' => 'Title', 'sortable' => true, 'filterable' => true])
        ->addField('p.createdAt', ['label' => 'Created', 'sortable' => true]);

    return $this->render('post/list.html.twig', [
        'grid' => $gridManager->getGrid($gridConfig),
    ]);
}
{{ kitpages_data_grid(grid) }}

If that renders a table with working pagination and header sort links, the integration is sound. Everything after this is customization.

Implementation Patterns

Controller-built grids vs. service-built grids. Building the GridConfig inline is fine for a one-off screen. For anything reused (an admin list reachable from several actions, or a grid embedded in a dashboard), extract a class that returns a configured GridConfig and register it as a service. The Kitpages lineage supports tagged grid services resolved by alias through the grid manager; check debug:container --tag for the exact tag name in this fork before relying on it. The win is that filters, sort whitelists, and column definitions live in one testable place.

QueryBuilder discipline. Treat the QueryBuilder as the contract. Select only the fields you render, keep the countFieldName on a unique, non-null column, and avoid GROUP BY unless you also fix the count query. A very common pattern:

$qb->select('p', 'a')            // entity + to-one join
   ->leftJoin('p.author', 'a')
   ->andWhere('p.deletedAt IS NULL');

Then define fields as p.title, a.name, p.createdAt so sorting maps directly to DQL aliases.

Filtering. Mark columns filterable => true for the built-in text filters. For anything more complex (date ranges, enums, relation filters), build the condition yourself from the request before constructing the datasource:

if ($status = $request->query->get('status')) {
    $qb->andWhere('p.status = :status')->setParameter('status', $status);
}

This keeps filter semantics in your domain code rather than fighting the bundle's filter layer.

Column rendering. Fields accept a format/value callback or rely on Twig block overrides. For badges, links, and formatted dates, override the bundle's templates under templates/bundles/KitpagesDataGridBundle/ — that is the least invasive extension point and survives a composer update. Per-field custom rendering via field options is preferable when only one column differs.

Multiple grids per page. Each grid reads pagination/sort/filter state from the query string. Two grids on one page will collide on page, sort, and filter parameter names. Isolate them by mounting the second grid on a distinct route (e.g. an ESI/fragment or an AJAX endpoint), or by verifying whether this fork supports a parameter prefix. Do not assume it does.

Array datasource for non-Doctrine data. When the source is an API response or a computed array, the array datasource keeps the same Twig rendering and pagination behavior. Sorting/filtering then happens in PHP, so keep the arrays small.

Layout integration. The default markup is plain HTML tables. If the project uses Bootstrap/Tailwind, override the layout template once and every grid inherits it. This is cheaper than per-grid markup tweaks and keeps accessibility attributes consistent.

Gotchas and Tips

Fork identity is the first trap. Composer package vysokeskoly/data-grid-bundle, repository KitpagesDataGridBundle, namespace Kitpages\DataGridBundle. Autoloading a class by the package name will fail; use the Kitpages\ namespace everywhere. Read the fork's composer.json before installing — it tells you the supported Symfony and Doctrine ranges, which is the single most useful compatibility signal for a package with no recent release.

Maintenance risk is real. Zero stars and a last release in April 2022 means no upstream fixes. Pin the version, commit the lock file, and assume you own any bug you hit. Before adopting, verify it installs against your Symfony version; if the constraints are stale, you may need composer require --ignore-platform-reqs (a warning sign) or a replace/conflict tweak.

The count query is the classic failure. getGrid() runs a separate COUNT query. With joins that multiply rows, or with GROUP BY, counts come back wrong and pagination silently truncates. Fix by counting on a distinct primary key (setCountFieldName('p.id')) and removing unnecessary joins from the count path. If counts still drift, dump the generated count SQL with the Doctrine logger.

Sorting is a whitelist, not a free-for-all. Only fields declared sortable => true are orderable. If a sort link does nothing, the field name in addField does not match the DQL alias the datasource produces — a typo in p.createdAt vs p.created_at produces a silent no-op rather than an error.

Filter parameters are request-driven. Confirm the fork binds filter values as parameters rather than interpolating them into DQL. If you cannot confirm, sanitize/validate filter input yourself before it reaches the datasource. This is a low-adoption package; do not assume a security review happened.

Pagination links drop your query string. Out of the box the generated links preserve only the grid's own parameters. If your page has other context (a search term, a tab, a parent ID), you will lose it on page 2. Handle this by putting grid state in the URL and rebuilding links, or by overriding the pagination template.

Debugging checklist. bin/console debug:container kitpages_data_grid to confirm services; bin/console debug:twig to confirm the render function name; enable Doctrine SQL logging to inspect the count and data queries; if the table is blank, check setCountFieldName first — a bad count field yields zero rows rather than an exception.

Extension points, in order of safety. (1) Field options and format callbacks, (2) Twig template overrides under templates/bundles/KitpagesDataGridBundle/, (3) a custom class implementing the datasource interface for non-Doctrine sources, (4) subclassing GridConfig/GridManager. Prefer the lowest number. Subclassing a bundle with no upstream activity locks you to its internals.

Plan an exit. Given the adoption profile, wrap grid construction behind your own small factory/service so the bundle appears in a handful of files. If it becomes unmaintained or blocks a Symfony upgrade, replacing it with a hand-rolled paginator or a maintained alternative is then a localized change rather than a project-wide rewrite.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin