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

Doctrine Datagrid Bundle Laravel Package

spyrit/doctrine-datagrid-bundle

Symfony bundle that adds a Doctrine-powered datagrid, turning entity queries into sortable, filterable, paginated tables with configurable columns. Integrates with Doctrine ORM and Symfony templates for building admin-style data listings.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle with Composer and register it in your Symfony kernel (skip the bundles.php step on Symfony Flex if auto-registration is enabled):

composer require spyrit/doctrine-datagrid-bundle

Add a minimal config block. The only thing most projects need is to point it at an entity manager:

# config/packages/spyrit_datagrid.yaml
spyrit_datagrid:
    driver: doctrine_orm
    entity_manager: default

Import the bundle's routing so the AJAX/refresh endpoints exist:

# config/routes/spyrit_datagrid.yaml
spyrit_datagrid:
    resource: "@SpyritDatagridBundle/Resources/config/routing.xml"
    prefix: /datagrid

Then the first, fastest use case: take one entity you already have a Repository for, annotate it as a grid, annotate the columns you want as sortable/filterable, and expose it through a controller that extends the bundle's base controller. Render it from a Twig template in one line.

use Spyrit\DatagridBundle\Annotation as Datagrid;

/** @Datagrid\Grid(name="product") */
class Product
{
    /** @Datagrid\Column(label="ID", sortable=true) */
    private $id;

    /** @Datagrid\Column(label="Name", sortable=true, searchable=true) */
    private $name;

    /** @Datagrid\Column(label="Price", sortable=true) */
    private $price;
}
class ProductController extends AbstractDatagridController
{
    protected function getGridName(): string
    {
        return 'product';
    }
}

The first payoff is a paginated, sortable, filterable table backed by Doctrine pagination with no hand-written QueryBuilder plumbing.

Where to look first in the source, in order:

  1. The annotation classes (Grid, Column) — this is where the configuration surface lives.
  2. The bundle's DependencyInjection/Configuration.php — what options are actually accepted.
  3. Resources/config/ — services and routing; tells you which extension points are public.
  4. Resources/views/ — the default Twig template you can copy and override.

Implementation Patterns

Annotation-driven grid definition. The bundle's core idea is that a grid is a view model over an entity, declared where the entity lives. Add @Grid at class level and @Column on properties. PHP 8 projects should register the bundle's attribute mapping and use #[Grid] / #[Column] if supported; otherwise fall back to doctrine/annotations.

One controller per grid, extending the base. The bundle expects a controller that returns a grid name (or the grid itself). Keep your CRUD controller and grid controller separate — the grid controller's job is only to render the datagrid view and delegate to the base's AJAX actions.

Multiple grids per entity. A single entity can carry more than one @Grid (e.g. product_list and product_admin), each with a different set of columns and defaults. Use this instead of overloading a single grid with conditionals.

Custom columns via closures. For computed values (formatted prices, status badges, action buttons, related counters) use a non-persisted column with a formatter closure. The closure receives the row entity, so you can call domain methods on it. Don't put DQL-heavy logic into the formatter — it runs per row.

/** @Datagrid\Column(label="Actions", type="closure") */
public function renderActions()
{
    return '<a href="' . $this->getId() . '">edit</a>';
}

Filtering and sorting as query state. The grid serializes column filters and sort order into the request and rebuilds the Doctrine QueryBuilder on each request. Wire your frontend to hit the datagrid route's AJAX endpoint (usually _ajax / _grid) for sort, filter, and pagination changes — don't re-render the full page for each interaction.

Overriding templates instead of patching the bundle. Copy the bundle's grid template into templates/bundles/SpyritDatagridBundle/ and edit it there. The bundle's Twig extensions render per-column fragments, so you usually only need to override one block (header sort links or a column type partial).

Integration with existing CRUD. The most common production pattern is: your standard indexAction renders a page shell; the grid itself is embedded via the bundle's Twig renderer, and all interactions are AJAX calls to the bundle's routes. Keep the shell's filters (date range, status) as normal request parameters and merge them into the grid via the controller — the bundle's own column filters won't cover joins efficiently.

Gotchas and Tips

Clear the metadata cache after annotation changes. Grid definitions are read as Doctrine-style metadata. The classic "I added a column and it didn't appear" bug is a stale metadata/annotation cache. Run bin/console cache:clear and, in Doctrine setups with a metadata cache, clear that too.

Column names must resolve as Doctrine fields or DQL aliases. If you sort on customerName and the field is a ManyToOne association, the bundle won't magically join for you. Either add an explicit join/field mapping in the column config, or denormalize. This is the single most common source of runtime DQL errors.

Association columns are the sharpest edge. Anything crossing an association ($product->getCategory()->getName()) will either need a join configured on the column or a closure column. Prefer the join path for sortable columns — closures can't be sorted server-side without a custom QueryBuilder.

CSRF and access control on AJAX routes. The datagrid endpoints are plain GET routes with sort/filter state in the query string. They are not protected by your entity voters by default. Add an explicit security voter check in your overridden controller (or a listener on the datagrid route) — otherwise filtered counts and pagination leak data across tenants.

Route and service names are not namespaced predictably. The bundle's service ids follow spyrit_datagrid.*. Dump the container (bin/console debug:container spyrit_datagrid) before assuming a service id; guessing gets you ServiceNotFoundException with no hint about the right name.

Config quirks. driver must match your ORM/ODM adapter — the bundle also ships (or historically shipped) Mongo support, and a mismatch fails silently at grid construction rather than at boot. entity_manager defaults to default; if you use multiple managers, set it explicitly per grid controller.

Default page size and sort are opt-in. Set sort and limit/pageSize on the grid annotation. Without an explicit default sort, pagination may fall back to database order and produce unstable pages — set a stable sort on the primary key.

Debugging. When the grid errors at render, check the Symfony profiler's Doctrine panel first: the generated query is usually visible there. If not, wrap the grid render in a controller and dump $grid->getQueryBuilder() to see what the bundle actually built before hydrating.

Extending the bundle safely. The extension points that are stable in practice: custom column types (subclass a column and tag it in the config), the base controller's getGrid() hook, the grid manager service, and Twig template overrides. Avoid patching the grid's internal QueryBuilder in a way that assumes a specific builder method unless you pin the version — the package is at low star count, so minor releases can shift internals.

Pin the version in composer.json. With zero dependents and modest maintenance, a floating ^ constraint against a bundle that touches Doctrine internals is asking for a broken upgrade. Use an explicit minor tag and read the changelog before bumping.

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.
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
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo