intonation/grid-bundle
A Symfony/PHP bundle for defining configurable data grids in code: declare columns, sorting, filtering, and pagination, then render them through templates. A practical starting point for admin tables and record listings, with room to customize how data is sourced and displayed.
The package wires up automatically via Laravel's package discovery; the only required step is composer require plus (if you want to tweak defaults) php artisan vendor:publish --tag=grid-config. No migrations are shipped — the grid library is metadata only and reads entities you already have.
Where to look first:
README.md quickstart — v0.2 rewrote this and it is now the fastest path to a working grid.config/grid.php — element/column guesser registration order, default pagination size, filter namespace.src/Grid/GridType — the base class/contract you extend for every grid.src/Grid/GridService.php — the new createGridFromEntity entry point.First use case — a read-only admin listing:
// app/Grid/UserGridType.php
class UserGridType extends AbstractGridType
{
public function buildGrid(GridBuilder $builder): void
{
$builder
->addColumn('id', IntegerType::class)
->addColumn('email', TextType::class)
->addColumn('created_at', DateTimeType::class)
->addFilter('email', TextType::class);
}
}
Route::get('/admin/users', fn (GridService $grids) =>
view('admin.users', ['grid' => $grids->createGrid('user_grid')])
);
For scaffolding, the new GridService::createGridFromEntity(User::class) gives you a usable grid with zero configuration — columns are derived from the entity, filters are inferred, and element types are resolved through ElementTypeGuesser. Use it to prototype, then promote to an explicit GridType once requirements firm up.
Explicit GridType per screen (recommended default). Register the type in config/grid.php (or via the service provider's registerGridType) and resolve it with GridService::createGrid('user_grid'). Keeps column/filter definitions testable and reviewable in isolation.
Entity-derived grids for internal tooling. createGridFromEntity is ideal for back-office tables where you control the schema but not the UI spec — CRUD scaffolds, generated admin panels, per-tenant tables built from a runtime entity list. Pair it with a small override callback (or a GridBuilder decorator) for the handful of columns that need custom rendering:
$grid = $grids->createGridFromEntity(Order::class);
$grid->getColumn('status')->setOptions(['format' => 'badge']);
Customizing element/column guessing. ElementTypeGuesser centralizes the "PHP type → form/grid element" mapping. Extend or decorate it and register with a priority higher than the built-in guessers when you need custom types (e.g. Money → a currency-aware column, Enum → a select filter with the enum cases). Keep custom guessers stateless — they're called for every column and filter.
Controller shape. Inject GridService rather than constructing grids inline; keep the action to three lines (create grid → handleRequest → return view/JSON). For AJAX-filtered tables, return the grid's data as JSON on X-Requested-With and render the full page otherwise — one endpoint, two responses.
Layer for reuse. If several grids share columns (timestamps, soft-delete badge, audit columns), compose them via a GridType trait or a GridBuilder decorator instead of duplicating the addColumn calls.
Testing. Unit-test buildGrid by asserting columns/filters/types on the builder output; functional-test the endpoint for filtering, sorting and pagination boundaries. For createGridFromEntity, assert against a factory-created model so schema drift is caught in CI.
ElementTypeGuesser registered after the defaults is dead code — set a priority or register before the package provider.$builder->setQueryBuilder(fn ($q) => $q->with('customer'))) rather than in the controller.sort query param straight into orderBy. Inferred grids in particular can expose join aliases you didn't intend.ElementTypeGuesser can change the resolved type for previously inferred columns (e.g. a DateTime field that used to fall back to a text input now resolving to a date picker). If a screen's filters or renderers are sensitive, pin the element type explicitly in the addColumn/addFilter call.$grid->getDefinition() or the equivalent) and confirm the type is registered and the name matches config; a missing registration usually surfaces as an empty or exception-throwing grid rather than a clear error.createGridFromEntity) rather than subclassing GridService — the service is the package's most likely surface to change in 0.x releases.How can I help you explore Laravel packages today?