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

Datagrid Bundle Laravel Package

internations/datagrid-bundle

Internations DataGrid Bundle is a Symfony bundle for defining and rendering reusable data grids, with configurable columns, sorting, filtering, and pagination for Doctrine-backed admin and listing pages.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, Symfony2-era bundle kept alive by InterNations. Treat the source as the documentation: read README.md, then DataGrid/DataGridFactory.php, DataGrid/DataGridBuilderInterface.php, DataSource/DoctrineORMDataSource.php, ColumnType/, and Resources/views/DataGrid/. The class namespace is DataGrid\… (not InterNations\…); confirm the bundle class name in vendor/internations/datagrid-bundle/DataGridBundle.php before registering it.

Version note (4.0.2). The only change in 4.0.2 is declared Symfony 5.4 compatibility. That is a constraint/metadata fix, not an API change — the grid API, service ids, and Twig theme are unchanged from 4.0.x. Practical reading: the supported target is Symfony 5.4 LTS. Nothing in the release claims Symfony 6.x or 7.x support, so treat 6+ as unverified and budget for the get()/public-service and return-type issues described in Gotchas before assuming it works.

Install and register

composer require internations/datagrid-bundle

Check composer.json constraints first: on 4.0.2 you should see symfony/* pinned in the ^5.4 range. If your app is on Symfony 6 or 7, either stay on 5.4 for this bundle, fork it, or pick a maintained alternative.

There is likely no Flex recipe, so register manually:

// config/bundles.php
return [
    // ...
    DataGrid\DataGridBundle::class => ['all' => true],
];

First use case: a Doctrine list page

Define a grid type exactly like a Symfony form type:

namespace App\DataGrid;

use DataGrid\DataGrid\AbstractDataGridType;
use DataGrid\DataGrid\DataGridBuilderInterface;

class ProductDataGridType extends AbstractDataGridType
{
    public function buildDataGrid(DataGridBuilderInterface $builder, array $options): void
    {
        $builder
            ->addColumn('id', 'number')
            ->addColumn('name', 'text')
            ->addColumn('price', 'money', ['currency' => 'EUR'])
            ->addColumn('createdAt', 'datetime', ['format' => 'Y-m-d H:i'])
            ->addColumn('actions', 'actions', [
                'actions' => [
                    'edit' => [
                        'route' => 'product_edit',
                        'route_parameters' => ['id' => 'id'],
                    ],
                ],
            ]);
    }
}

Create it in the controller from a Doctrine QueryBuilder:

$qb = $productRepository->createQueryBuilder('p');

// Service id in the bundle is typically data_grid.factory.
// In modern Symfony, alias/inject DataGrid\DataGrid\DataGridFactoryInterface if autowiring is set up.
$grid = $this->container->get('data_grid.factory')
    ->createDataGrid(ProductDataGridType::class, $qb);

return $this->render('product/index.html.twig', ['grid' => $grid]);

Render it with the Twig function:

{{ datagrid(grid) }}

That single call gives you a sortable, paginated Bootstrap table. If that is all you need, adoption is quick.

Implementation Patterns

Grid types as reusable UI components

Mirror the form-type workflow: one class per list view, options passed through createDataGrid($type, $data, $options), defaults in configureOptions(OptionsResolver $resolver). This keeps controller actions thin and makes grids testable and reusable across admin and public controllers.

Choose or write a data source

The bundle centres on a DataSourceInterface; the Doctrine ORM source accepts a QueryBuilder, and there are collection/array sources for in-memory lists. For APIs, DBAL, or Elasticsearch, implement the interface:

use DataGrid\DataSource\DataSourceInterface;
use DataGrid\DataGrid\DataGridInterface;

final class ApiDataSource implements DataSourceInterface
{
    public function getData(DataGridInterface $dataGrid): array
    {
        return $this->client->fetch([
            'page'  => $dataGrid->getPage(),
            'limit' => $dataGrid->getMaxPerPage(),
            'sort'  => $dataGrid->getSortField(),
            'order' => $dataGrid->getSortOrder(),
        ]);
    }

    public function getTotalCount(DataGridInterface $dataGrid): int
    {
        return $this->client->count();
    }
}

Verify exact method names against the installed interface before writing this — this is the kind of package where signatures matter more than docs.

Sorting and pagination

Sorting is driven by request query parameters (sort, order, page, and a limit parameter) that the bundled template emits and the factory/grid reads. Mark columns sortable (or leave the default) and set sensible defaults in buildDataGrid if the builder exposes setDefaultSort() / setMaxPerPage():

$builder->setMaxPerPage(25);
$builder->setDefaultSort('createdAt', 'desc');

Because sorting is request-driven, the controller usually only needs to build the QueryBuilder; the grid applies orderBy and limit/offset. That is the main day-to-day win.

Actions and translations

The actions column type maps row properties to route parameters. The values in route_parameters are property names on the row, not literals — so ['id' => 'id'] means “take the row’s id getter”. The translation column type translates a value with the translator, with a domain option. Both types are good candidates for copying if your UI needs different markup.

Themes and custom rendering

The bundle ships Twig themes (typically Bootstrap 3 markup, no CSS). For Bootstrap 5 or Tailwind, copy the bundled template into your app, adjust classes, and point the bundle’s data_grid theme configuration at it — confirm the exact config key in DependencyInjection/Configuration.php. If the theme system fights you, bypass it: the grid object exposes columns, data, page, and sort state, so you can render your own table in a few lines of Twig.

Multiple grids and filters

For dashboards with several grids, check whether the grid supports distinct request parameter names. If not, put each grid on its own route or namespace the query parameters yourself. There is no built-in filter/search layer — filters belong in the QueryBuilder you pass in (read $request->query and add andWhere clauses). If you do this, make sure the theme’s sort/pagination links preserve your filter parameters, otherwise paging will silently drop them.

Testing

Boot a kernel test, fetch the factory, and build the grid with an ArrayDataSource:

$grid = $factory->createDataGrid(ProductDataGridType::class, new ArrayDataSource($fixtures));

$this->assertSame(['id', 'name', 'price'], array_keys($grid->getColumns()));
$this->assertStringContainsString('Widget', (string) $grid->render());

Unit-test custom column types and data sources directly; functional-test rendering with a request carrying page/sort parameters.

Pin the patch line

4.0.2 is a compatibility patch, so it is the safe floor for a Symfony 5.4 app. Pin ^4.0 (or 4.0.*) in composer.json and commit the lock file — with one maintainer-adjacent package and no SemVer guarantees documented in the README, an unpinned constraint is how you discover a behaviour change in production.

Gotchas and Tips

  • Symfony 5.4 only (per 4.0.2). The release explicitly targets Symfony 5.4. Symfony 6/7 removed or changed things this bundle depends on — public service access via $this->get(), non-typed setters, possibly getMockBuilder-era test helpers. Do not assume it installs or boots on 6+. If you must, audit services.xml/services.yml for deprecated aliases and missing return types before upgrading the framework.
  • 4.0.2 brings no fixes. The release note is compatibility metadata only: no bug fixes, no new column types, no theme changes, no deprecations. If you were waiting on a specific fix, it is still pending — keep your local patches and open issues.
  • Ancient API, modern Symfony. Service IDs like data_grid.factory are old-style. On Symfony 5.4, $this->get() will fail unless the service is public; add an alias to DataGridFactoryInterface in services.yaml or configure the service as public. Do not assume autowiring aliases exist.
  • Tiny community. One star and zero dependents means no Stack Overflow answers and sparse docs. Budget time to read source and pin the version. The 4.0.2 release indicates InterNations still uses it on 5.4, but you are largely on your own.
  • Count queries are the usual Doctrine trap. getTotalCount() rewrites or clones the QueryBuilder. groupBy, having, distinct, fetch joins, and complex select() clauses can produce wrong counts or SQL errors. For large or complex queries, implement a custom data source with an explicit count query; COUNT(*) on big tables is also slow — cache or approximate where possible.
  • Mutating the QueryBuilder. The ORM data source applies orderBy, setFirstResult, and setMaxResults to the builder you hand it. Clone before reuse, or you will get paginated results in unrelated code paths.
  • Sorting on joined or aliased fields. The column name usually maps to a DQL field on the root alias. Sorting by category.name or a computed field needs a sort-field mapping option or a custom data source; otherwise you get “unrecognized field” errors.
  • Pagination collisions. Two grids on one page will fight over page/sort/order unless parameter names are configurable. Check the grid/template source before designing a dashboard.
  • Links drop filter state. The bundled sort and pagination links typically rebuild from the current query string, but custom themes or hand-written links easily lose filters. Test with a filter applied and page 2.
  • Hydration and column getters. The default ORM hydration returns entities; text/number columns use getters or property access. Partial selects or array hydration will break columns. Use the array column type or a custom column for arrays.
  • Escaping and HTML. Text columns escape output; if you need HTML, use a Twig/HTML column type or a custom template block and escape deliberately. Do not disable escaping globally.
  • Formatting details. Date/datetime and money types have their own options (format, currency, locale behaviour). Verify timezone handling and whether money expects minor units. Date formats using PHP date() patterns differ from Intl patterns.
  • Actions throw on bad getters. A missing row getter referenced by route_parameters fails at render time. Keep action definitions close to the entity and test them.
  • No filters, exports, or mass actions. This bundle is a thin sortable/paginated table. If your roadmap includes faceted filtering, column selection, CSV export, or bulk operations, either build them yourself around the query builder or look at Sylius Grid / EasyAdmin.
  • Empty results and pagination edge cases. Zero rows can produce awkward “page 1 of 0” output; check the template block and handle it in a custom theme.
  • Extension points to know. Custom column types are the cleanest extension point (usually registered with a container tag such as data_grid.column_type, aliased to a type name); custom data sources cover new backends; custom grid types cover shared configuration. Overriding the theme is the escape hatch for markup.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect