Installation:
composer require sonata-project/datagrid-bundle
Add to config/bundles.php:
SonataProject\DatagridBundle\SonataDatagridBundle::class => ['all' => true],
First Use Case:
Create a basic grid for an App\Entity\User entity:
# config/packages/sonata_datagrid.yaml
sonata_datagrid:
grids:
user:
model: App\Entity\User
actions:
_list:
subactions:
show: ~
edit: ~
delete: ~
Controller Integration:
use Sonata\DatagridBundle\Datagrid\ProxyQueryInterface;
use Sonata\DatagridBundle\Datagrid\DatagridMapper;
public function listAction(ProxyQueryInterface $datagrid)
{
$datagrid->setModelManager($this->getDoctrine()->getManager());
$datagrid->setPager($this->get('sonata.datagrid.pager'));
$datagrid->initialize();
return $this->render('user/list.html.twig', [
'datagrid' => $datagrid,
]);
}
Twig Template:
{% for user in datagrid %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.email }}</td>
<td>{{ user.createdAt|date('Y-m-d') }}</td>
</tr>
{% endfor %}
vendor/sonata-project/datagrid-bundle/Resources/config/services.yaml.vendor/sonata-project/datagrid-bundle/Tests/App/Resources/config/sonata_datagrid.yaml.Define Grid in YAML/XML:
grids:
advanced_user:
model: App\Entity\User
fields:
id: ~
email:
type: string
template: 'user/field_email.html.twig'
roles:
type: twig
template: 'user/field_roles.html.twig'
options:
function: 'getRolesAsString'
properties:
createdAt: ~
group_by: ['roles']
sort_by: ['createdAt', 'DESC']
Dynamic Field Mapping:
$datagrid->addField('custom_field', 'string')
->setTemplate('user/custom_field.html.twig')
->setOptions(['function' => 'formatCustomField']);
Pagination & Sorting:
sonata_datagrid:
grids:
paginated_user:
model: App\Entity\User
pager:
items_per_page: 20
sort_by: ['id', 'DESC']
Filtering:
filters:
user:
type: doctrine_orm
field: email
options:
label: 'Email'
placeholder: 'Filter by email...'
With Sonata Admin:
Extend Sonata\AdminBundle\Datagrid\DatagridMapper for admin-specific grids:
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('id', 'doctrine_orm', ['field' => 'id']);
}
Custom Query Logic:
Override the query via setQuery():
$datagrid->setQuery($datagrid->getQuery()->where('u.active = 1'));
AJAX Support:
Use sonata.datagrid.pager.ajax for infinite scroll:
{{ sonata_datagrid_pager_ajax(datagrid, 'user_list') }}
Reusable Components: Create base grid classes for shared logic:
class BaseUserGrid extends AbstractGrid
{
public function configure()
{
$this->addField('id')->setSortable('id');
$this->addField('email')->setSortable('email');
}
}
Deprecation Warnings:
KnpPaginatorBundle (for pagination).EasyAdminBundle (for admin grids).API Platform (for data grids in APIs).Doctrine Proxy Issues:
ProxyQueryInterface is injected correctly:
$datagrid->setModelManager($this->getDoctrine()->getManagerForClass(User::class));
Caching Quirks:
php bin/console cache:clear
Field Type Conflicts:
text vs. sonata_type_text).Query Dumping: Enable Doctrine debugging to inspect generated queries:
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
Grid Dumping: Log the grid configuration for debugging:
\Symfony\Component\Debug\Debug::dump($datagrid->getConfiguration());
Common Errors:
setModelManager() is called.pager: ~ to grid config.Performance:
DISTINCT for grouped queries:
group_by: ['department']
distinct: true
SELECT *:
fields:
id: ~
name: ~
Extending:
Sonata\DatagridBundle\Type\Field\FieldTypeInterface.class CustomFieldType extends AbstractType
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['custom_option'] = $options['custom_option'];
}
}
Testing:
ProxyQueryInterface in PHPUnit:
$datagrid = $this->createMock(ProxyQueryInterface::class);
$datagrid->method('getQuery')->willReturn($queryBuilder);
Legacy Migration:
sonata_admin to sonata_datagrid:
# Before (Sonata Admin)
sonata_admin:
grids:
user:
fields: [...]
# After (Sonata Datagrid)
sonata_datagrid:
grids:
user:
model: App\Entity\User
fields: [...]
How can I help you explore Laravel packages today?