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

kibatic/datagrid-bundle

Kibatic DatagridBundle is a Symfony/PHP bundle for building reusable, configurable data grids from Doctrine queries or arrays. Define columns, filters, sorting, and pagination with minimal code, then render clean tables for admin, reporting, and data-heavy screens.

View on GitHub
Deep Wiki
Context7

Getting Started

Clear the framework question before writing any code. kibatic/datagrid-bundle is a Symfony bundle, not a Laravel package. There is no DatagridServiceProvider, no package auto-discovery, no config/datagrid.php, and no facade. composer require kibatic/datagrid-bundle in a Laravel app will attempt to pull symfony/framework-bundle, symfony/twig-bundle, and most likely doctrine/orm — none of which your Eloquent/Blade app uses. Decide up front which of three paths you are on:

  1. Reimplement the concepts (most common correct answer). Read the bundle's grid/column/filter classes, then write ~200 lines of your own App\Support\DataGrid bound to Eloquent + Blade. With 14 stars and zero dependents, this is frequently cheaper than integration.
  2. Instantiate the grid classes directly in a Laravel controller (new ProductGrid(...)) if composer.json shows the grid classes are framework-light. You skip the kernel entirely and hand-wire dependencies.
  3. Sidecar Symfony micro-app hitting the same database, rendered in an iframe. Only justified if the Symfony app already exists.

Minimal steps for path 2 (direct instantiation):

composer require kibatic/datagrid-bundle --dry-run   # inspect the resolve first

Then read, in this order:

  • composer.json → the require block. If symfony/framework-bundle or doctrine/orm are hard requirements, path 2 is likely dead and you drop to path 1.
  • src/Resources/config/services.xml (or .yaml) → the authoritative list of service IDs and constructor arguments. This tells you exactly what a grid object needs handed to it.
  • DependencyInjection/*Extension.php → the bundle's config tree (kibatic_datagrid: keys, default page size, template namespace). This is the "config file" you would normally publish — it has no Laravel equivalent, so you translate it into a config/datagrid.php you own.
  • src/ → the base grid class plus Column, Filter, and any factory types.
  • Resources/views/ → the Twig templates you will reimplement in Blade.

First use case: a single read-only admin index over one table — paginated, sortable on two or three columns, one text filter, no export, no inline edit. Resist building a full CRUD grid first; you will spend the day on the integration seam rather than on the grid.

If you commit to the Doctrine route (path 2 with laravel-doctrine/orm), budget a day for the entity manager wiring before you declare a single column.

Implementation Patterns

Grid-as-a-class, controller stays thin. Whatever the base class is called, subclass it per resource and declare columns, filters, and default sort in one place:

final class ProductGrid extends AbstractGrid
{
    public function build(): void
    {
        $this->addColumn('name', sortable: true, filter: 'like');
        $this->addColumn('status', sortable: true, filter: 'choice',
            choices: ['active', 'archived']);
        $this->addColumn('created_at', label: 'Created', sortable: true);
        $this->setDefaultSort('created_at', 'desc');
    }
}

The controller does nothing but instantiate, hydrate from request input, and hand off to the view. Every screen that needs a grid follows the same shape, which is the whole point of adopting this rather than hand-rolling.

Wrap it behind your own interface. This is the deliverable regardless of which path you take:

interface GridContract {
    public function data(): LengthAwarePaginator;
    public function columns(): array;
    public function sort(): array;
    public function filters(): array;
}

If the bundle proves unworkable — or the maintainer disappears, which is a live risk at 14 stars — you swap one binding in a service provider, not fifteen controllers.

Eloquent adapter (path 1 or 2). The bundle will not speak Eloquent. Build a thin translator from grid state to query builder:

  • sort → ->orderBy($column, $direction), with the column validated against the declared sortable set.
  • filter[status]=active → ->where('status', $value); like filters → ->where($col, 'like', "%{$v}%").
  • pagination → ->paginate($perPage)->withQueryString().

Keep the translator pure and unit-test it; it is where bugs live.

Preserve query state across sort/filter/pagination clicks. Every column header link and every pager link must merge the current query string, or filters silently reset when the user sorts:

$sortUrl = request()->fullUrlWithQuery(['sort' => 'name', 'dir' => 'asc']);

This is the single most common day-to-day bug in server-rendered grids.

Blade renderer, not Twig. Either port Resources/views/*.twig to a Blade partial (they are simple loops and conditionals — {{ }} → {{ }}, |raw → {!! !!}), or drive Twig standalone via twig/twig and Twig\Environment with FilesystemLoader pointed at the bundle's Resources/views. The Blade port is usually faster and avoids a second template engine in the app.

Reuse the grid for export. Same grid class, same filters, ->get() with the pagination stripped, streamed to CSV. This is the real payoff of a declared grid: filter logic is written once and reused by screen and export.

Service provider wiring. Bind GridContract per resource via contextual binding, or tag and resolve. Keep the binding explicit — do not attempt a Symfony-style tag compiler pass in Laravel.

Testing pattern. Feature-test the grid endpoint with real query strings:

$this->get('/admin/products?sort=name&dir=asc&filter[status]=active')
    ->assertOk()
    ->assertSeeInOrder(['Alpha', 'Beta']);

Assert the generated SQL with ->toSql() on the underlying builder for the translator unit tests.

Do not run this alongside Inertia/Livewire tables. Two table implementations in one app is a maintenance tax with no upside. If a JS data table already exists, this package's role is reference material only.

Gotchas and Tips

It is a Symfony bundle — assume nothing installs cleanly. composer require may demand symfony/http-foundation or symfony/http-kernel versions that conflict with what a Laravel app already pulls in transitively. Run --dry-run first, and --with-all-dependencies if you must. If resolution requires downgrading a core dependency, stop — a 14-star package is not worth that.

Whitelist sort and filter keys, always. The temptation with a generic grid is to pass ?sort= straight to orderBy(). That is SQL injection. Validate against the declared sortable columns and reject unknown keys with a 422. Filter names likewise.

N+1 queries hide in columns. A column rendering $row->customer->name will fire one query per row. Eager-load in the grid's base query (with(['customer'])), not in the view. Load-test with DB::listen() before shipping, or you will find out in production.

OFFSET pagination degrades. Sorting by an unindexed column then paginating to page 400 does a full scan in both MySQL and Postgres. Verify EXPLAIN on your default sort + filter combination. For deep datasets, keyset pagination on created_at is the fix; the bundle will not do it for you.

Doctrine is likely a hard dependency. If doctrine/orm appears in require (not require-dev), path 2 requires laravel-doctrine/orm, an entity manager, and entity mappings parallel to your Eloquent models. That is a lot of infrastructure for a table. Check before committing.

Twig templates will not just work. If you render them with standalone Twig, remember the bundle may reference Symfony Twig extensions (path(), asset()) that do not exist in a Laravel context. Expect to stub or rewrite those calls.

Pin the exact version. Note the anomalous future-dated release timestamp in the package metadata — verify the actual tag on GitHub before trusting it. Small packages ship breaking changes in patch versions. Use "kibatic/datagrid-bundle": "1.2.3" (exact), commit composer.lock, and treat upgrades as deliberate work.

Assume you own the code. 14 stars, 0 dependents, single maintainer. There is no community to consult, no Stack Overflow corpus, no issue-response SLA. MIT licensing makes a private fork legally trivial and is your actual mitigation — vendor the fork under packages/ with a path repository the moment you patch it rather than configuring it.

Debugging the generated query. In Symfony you would dump() the query builder; in Laravel use ->toSql() plus ->getBindings() on the translator's output, or wrap the endpoint in DB::listen(fn ($q) => logger()->info($q->sql, $q->bindings)). Ninety percent of "the grid is wrong" reports are a bad filter binding or a lost query string.

Extension points, if you stay on the bundle classes. Columns are typically subclassable with an overridable render() — that is the seam for badges, date formatting, and action-link cells. Prefer a custom column over stuffing HTML into a label, and prefer a Blade component over a custom column where the framework allows it.

Filters must be GET, not POST. Server-rendered grids live and die by shareable/bookmarkable URLs. If you find yourself POSTing a filter form, the pagination and sort links will break in ways that are painful to unwind.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle