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

samson/data-grid-bundle

samson/data-grid-bundle is a Symfony bundle for building configurable data grids with sorting, filtering, pagination, and custom columns. It helps present Doctrine entities and other data sources in admin panels, dashboards, and reports.

View on GitHub
Deep Wiki
Context7

Getting Started

Before anything else: this is a Symfony2 bundle, not a Laravel package. samson/data-grid-bundle is a Symfony bundle that ships AngularJS 1.x directives plus a PHP grid-definition service. It has no Laravel service provider, no facade, and no config/data-grid.php. If you are on Laravel, the realistic options are (a) port the idea into a Blade + Alpine/Livewire component, or (b) skip it — see "If you're actually on Laravel" below. The rest of this section assumes you have inherited or are evaluating a Symfony 2.x app.

Prerequisites

  • Symfony 2.3–2.8 (the bundle's last release is 2015-03-03; expect constraints against symfony/symfony: ~2.x).
  • AngularJS 1.x loaded on the page (the bundle does not bundle a modern Angular).
  • PHP 5.5–7.0 era code; on PHP 7.1+ you will likely hit deprecations and possibly fatal errors.

Minimal install

composer require samson/data-grid-bundle
# if no matching stable tag for your Symfony version:
composer require samson/data-grid-bundle:dev-master

Register it in app/AppKernel.php:

public function registerBundles()
{
    $bundles = [
        // ...
        new Samson\DataGridBundle\SamsonDataGridBundle(),
    ];
}

Publish the JS/CSS out of Resources/public so the browser can load it:

php app/console assets:install web --symlink
php app/console cache:clear

Where to look first, in this order

  1. Samson/DataGridBundle/Resources/config/services.yml — the grid service ID and its constructor arguments. This tells you what a "grid" object actually is.
  2. Samson/DataGridBundle/Resources/public/js/ — the AngularJS module, directive names, and the $http call. This is the real API contract: what URL it hits and what JSON shape it expects.
  3. DependencyInjection/ — whether columns are configurable via config.yml or only in PHP.
  4. Resources/views/ — the Twig template(s) you will be overriding.

First use case: a read-only, paginated, sortable listing over Doctrine. Define the columns in PHP, expose one JSON endpoint, and let the directive render:

// AcmeBundle/Controller/UserGridController.php
public function gridAction(Request $request)
{
    $grid = $this->get('samson_data_grid.factory')  // check services.yml for the real ID
        ->create('users')
        ->addColumn('id',        ['label' => '#',    'sortable' => true])
        ->addColumn('email',     ['label' => 'Email','sortable' => true])
        ->addColumn('createdAt', ['label' => 'Created','type' => 'datetime', 'sortable' => true]);

    return $this->json($grid->getResponse($request)); // or new JsonResponse(...)
}
{# AcmeBundle/Resources/views/User/list.html.twig #}
<div ng-app="samsonDataGrid"
     ng-controller="DataGridCtrl"
     data-grid-url="{{ path('acme_user_grid') }}">
</div>

If the rendered markup is empty and you see a request in the Network tab returning JSON, you are 90% done — the rest is column config.

If you're actually on Laravel: do not install this. The equivalent day-to-day stack is yajra/laravel-datatables (server-side, jQuery), Livewire + wire:model for filters, or Inertia + a Vue/React table. The only thing worth stealing from this bundle is the pattern: define columns once in PHP, serialize them alongside the rows, and let the front-end render generically. That pattern survives the framework translation; the code does not.

Implementation Patterns

Treat the PHP grid definition as the single source of truth. The bundle's value is that columns, labels, sortability, and formatters live in one place and are serialized to the client. Resist the urge to also hand-write <th> cells in Twig — the moment the two drift, sorting breaks. If you need a header outside the grid, derive it from the same grid object.

Server-driven pagination/sorting/filtering. The directive will send query params; pin down the contract early and whitelist everything server-side:

$page   = max(1, (int) $request->query->get('page', 1));
$limit  = min(100, max(10, (int) $request->query->get('limit', 25));
$sort   = $request->query->get('sort', 'id');
$dir    = strtolower($request->query->get('dir', 'asc')) === 'desc' ? 'DESC' : 'ASC';

$allowed = ['id', 'email', 'createdAt'];           // never interpolate raw input
if (!in_array($sort, $allowed, true)) { $sort = 'id'; }

$qb = $em->createQueryBuilder()
    ->select('u')->from('AcmeBundle:User', 'u')
    ->orderBy('u.'.$sort, $dir)
    ->setFirstResult(($page - 1) * $limit)
    ->setMaxResults($limit);

Return a stable envelope — { rows: [...], total: N, page: P, limit: L } — because the directive and your tests both depend on it.

Keep formatters on the client, business logic on the server. Date/number/currency formatting belongs in Angular filters or a small JS formatter registry. Anything that touches a relation, a permission check, or a computed total belongs in a DQL addSelect or a serializer, not in a JS cellRenderer.

Avoid N+1 by shaping the query, not the formatter. The classic failure mode of PHP-defined grids is a getCompanyName() column that lazy-loads per row. Add a join up front:

->addSelect('c')->leftJoin('u.company', 'c')

and expose u.company.name as a flattened field. On a 25-row page this is invisible; on limit=100 over a slow link it is a 100-query request.

Multi-grid pages: isolate scopes. Each grid should have its own ng-controller (or a component) with its own URL and its own state. Sharing a top-level controller across two grids means a filter on one re-renders the other.

Overlay features the bundle doesn't have. This is a 2015 read-only grid; assume no inline editing, no column chooser, no bulk actions. Add row actions as an extra column rendered by your own template, and drive them with a $scope.$emit back to a parent controller. Don't fork the bundle's JS to add them — you will never merge upstream again.

Test the endpoint, not the widget. A Symfony functional test against the JSON route gives you 95% of the confidence for 5% of the effort:

$client->request('GET', '/grid/users?page=2&sort=email&dir=desc');
$data = json_decode($client->getResponse()->getContent(), true);
$this->assertCount(25, $data['rows']);
$this->assertSame('desc', $data['dir']);

Add one assertion that a hostile sort=email);DROP value is ignored — that's your regression test for the whitelist.

Embed config, don't duplicate it. If the grid needs to be re-initialized after an AJAX page swap, emit the grid config as a data- attribute or a <script type="application/json"> block rather than re-fetching it or hardcoding it in JS.

Gotchas and Tips

Both halves of the stack are end-of-life. Symfony 2.x reached EOL in November 2018; AngularJS 1.x reached EOL on 31 December 2021. There are no security patches for either. Treat this as a "keep the lights on in a legacy app" tool, never as a foundation for new work. Put a date on the migration ticket the day you install it.

Composer will fight you. A 2015 bundle will not resolve against modern Symfony. --ignore-platform-reqs and --no-scripts are how people get this installed, and both are how people end up with a broken composer.lock. Install it with composer require ... --update-with-dependencies scoped to just the affected packages, and check git diff composer.lock before committing.

Assets are the #1 "it doesn't work" cause. The bundle's JS lives in Resources/public and is not web-accessible until assets:install runs. In dev, use --symlink so edits to vendor JS show up without re-running it. In prod, remember it's a separate deploy step from cache:clear.

AngularJS digest problems. If you trigger a reload from non-Angular code (jQuery event, setTimeout outside $apply), the data arrives but the DOM doesn't update. Wrap in $scope.$apply() or, better, use $timeout. Also check whether jQuery is loaded before Angular — with jQuery present, Angular uses full jQuery and angular.element behaves differently than with jqLite.

Ordering by a relation silently breaks. orderBy('u.company.name') requires the join to exist in the same QueryBuilder; Doctrine will throw or, worse, sort by company_id. Always join before sorting on a joined alias.

total is the expensive part. COUNT(*) over a filtered, joined query can dominate the response time. If the grid gets slow, that's the first query to profile — consider an approximate count or caching the count per filter-set.

Date and timezone drift. PHP serializes DateTime in the server timezone; Angular's date filter renders in the browser's. A grid that looks correct for you will be off by hours for a user in another region. Serialize to ISO-8601 with an explicit offset (or UTC) and format client-side.

Whitelist sort and dir — this is DQL injection. There is no framework protection here; orderBy takes a raw string. The bundle almost certainly does not sanitize for you. Do it in your controller.

CSRF and the firewall. The JSON endpoint is hit by $http GETs, which is fine, but any state-changing action you bolt onto the grid (delete, bulk update) needs a CSRF token and the correct security.yml pattern for the route — don't accidentally exclude it from the firewall to "make the AJAX work."

Override templates the Symfony way. Don't edit vendor/. Copy the template to app/Resources/SamsonDataGridBundle/views/ and edit there; Symfony's bundle inheritance picks it up. Same for JS: copy the file into your own AcmeBundle/Resources/public/js/ and load that instead if you need to change behavior.

Debugging order of operations: Network tab (is the JSON correct?) → console (is the directive bound?) → {{ grid_config|json_encode }} in Twig (did the server pass the config?) → only then the bundle's JS. Roughly 80% of "the grid is broken" reports resolve at step one.

Extension points worth knowing: the grid service (decorate it to add global column defaults), the Twig template (bundle inheritance), and the column formatter registry (add your own types). There is no event system to hook; if you need lifecycle hooks, you're modifying the service, which means a fork — budget for that before you commit to the bundle.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource