Installation:
composer require sonata-project/admin-bundle
Add the bundle to config/bundles.php:
return [
// ...
SonataAdminBundle\SonataAdminBundle::class => ['all' => true],
];
Enable CRUD for a Model:
Create a CrudController for your entity (e.g., src/Controller/DemoController.php):
use Sonata\AdminBundle\Controller\CRUDController;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
class DemoController extends CRUDController
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('fieldName');
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('fieldName');
}
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper->add('fieldName');
}
}
Register the Admin in Services:
# config/packages/sonata_admin.yaml
sonata_admin:
options:
html5_validate: true
security:
handler: sonata.admin.security.handler.role
assets:
less:
output_directory: '%kernel.project_dir%/public/build'
Route the Admin:
# config/routes.yaml
sonata_admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin
First Use Case:
Access /admin/demo to see the auto-generated CRUD interface for your entity.
src/Controller/: Look for existing *Admin.php controllers to understand how CRUD is structured.config/packages/sonata_admin.yaml: Default configuration for the bundle.var/cache/dev/: Clear this directory (php bin/console cache:clear) if changes aren’t reflected.Admin Class Generation:
Use the sonata-project/admin-generator-bundle (deprecated in favor of manual controllers) or scaffold via:
php bin/console generate:sonata-admin
(Note: Modern workflows favor manual CrudController extensions.)
Customizing CRUD:
Override methods in your CrudController:
// Customize list view
protected function configureDatagridFilters(DatagridMapper $datagridMapper) { ... }
// Customize form fields
protected function configureFormFields(FormMapper $formMapper) { ... }
// Customize show action
protected function configureShowFields(ShowMapper $showMapper) { ... }
Field Types and Options:
Use Sonata’s built-in field types (e.g., sonata_type_model, sonata_type_datepicker) or extend them:
$formMapper->add('tags', 'sonata_type_model', [
'btn_add' => 'Add',
'btn_delete' => 'Remove',
'btn_list' => 'List',
'model_manager' => $this->getModelManager(),
]);
Access Control: Restrict admin access via YAML:
sonata_admin:
security:
handler: sonata.admin.security.handler.role
admin_access:
- ROLE_ADMIN
Or dynamically in configureRoutes():
public function configureRoutes(RouteCollection $collection)
{
$collection->remove('delete');
}
Batch Actions:
Enable batch operations in configureDatagridFilters:
$datagridMapper->add('isActive', 'boolean', [
'label' => 'Active',
'template' => 'SonataAdminBundle:CRUD:base_list_field.html.twig',
'editable' => true,
]);
Integration with Doctrine: Sonata works seamlessly with Doctrine ORM. For custom repositories:
public function getRepository()
{
return $this->getModelManager()->getRepository('App\Entity\Demo');
}
Theming:
Override Twig templates in templates/SonataAdmin/ or extend base templates:
{# templates/SonataAdmin/CRUD/base_list_field.html.twig #}
{% extends 'SonataAdmin:CRUD:base_list_field.html.twig' %}
FormBuilder.$formMapper->add('name', null, [
'label' => 'admin.demo.name',
]);
// src/EventListener/DemoListener.php
public function prePersist($admin, $object)
{
$object->setCreatedAt(new \DateTime());
}
Register in services.yaml:
services:
App\EventListener\DemoListener:
tags:
- { name: sonata.admin.event_listener, admin: demo_admin, event: prePersist }
Caching Issues:
php bin/console cache:clear
sonata.cache.clear event if needed.Route Conflicts:
sonata_admin:
options:
base_route_pattern: 'app_admin'
base_route_name: 'app_admin'
Doctrine Proxy Classes:
fetch=EAGER or customize hydrators:
$this->getModelManager()->getHydrator()->setHydrateOnLoad(true);
Translation Misconfigurations:
# config/packages/sonata_admin.yaml
sonata_admin:
options:
translation_domain: 'sonata_admin'
Deprecated Methods:
configureListFields() (use configureDatagridFilters() + configureListFields() separately).FormMapper over sonata_type_model for complex relations.Enable Debug Mode:
# config/packages/dev/sonata_admin.yaml
sonata_admin:
debug: true
Logs admin actions to var/log/dev.log.
Check Admin Registration: Verify your admin is registered in services:
php bin/console debug:container sonata.admin.demo
Template Overrides:
Use debug:twig to check if templates are being overridden:
php bin/console debug:twig SonataAdminBundle:CRUD:list.html.twig
Database Queries: Enable Doctrine profiling to debug N+1 queries:
// config/packages/dev/doctrine.yaml
doctrine:
profiler: true
Custom Field Types:
Extend Sonata\AdminBundle\Form\Type\BaseType to create reusable fields:
class CustomFieldType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) { ... }
}
Register in services.yaml:
services:
App\Form\Type\CustomFieldType:
tags:
- { name: sonata.admin.form.type, model: App\Entity\Demo, group: custom }
Dynamic Admin Classes:
Use sonata.admin.abstract_admin to dynamically generate admins:
services:
app.demo_admin:
class: App\Admin\DemoAdmin
arguments: ['null', 'App\Entity\Demo', 'AppController']
tags:
- { name: sonata.admin, manager_type: orm, group: Content, label: Demo }
API Integration:
Combine with sonata-project/google-maps-bundle or api-platform for geo-data or API-driven CRUD.
Custom Actions:
Add custom routes/actions in configureRoutes():
public function configureRoutes(RouteCollection $collection)
{
$collection->add('export', $this->getRouterIdParameter() . '/export');
}
Handle the action in preUpdate()
How can I help you explore Laravel packages today?