aschaeffer/sonata-admin-bundle
Installation:
composer require sonata-project/admin-bundle sonata-project/doctrine-orm-admin-bundle
Add bundles to config/bundles.php:
return [
// ...
Sonata\AdminBundle\SonataAdminBundle::class,
Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle::class,
];
Enable in config/packages/sonata_admin.yaml:
sonata_admin:
title: 'My Admin Panel'
title_logo: 'bundles/sonataadmin/logo.svg'
options:
html5_validation: true
First Admin Class:
// src/Admin/UserAdmin.php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use App\Entity\User;
class UserAdmin extends AbstractAdmin
{
protected $datamodel = User::class;
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter->add('name');
}
protected function configureListFields(ListMapper $list): void
{
$list->add('id');
$list->add('name');
$list->add('email');
}
protected function configureFormFields(FormMapper $form): void
{
$form->add('name');
$form->add('email');
}
}
Register Admin in services.yaml:
services:
App\Admin\UserAdmin:
tags:
- { name: sonata.admin, manager_type: orm, group: 'Users', label: 'User' }
Access Admin Panel:
Visit /admin (ensure sonata_admin route is enabled in config/routes.yaml).
Post).AbstractAdmin for PostAdmin./admin/app_post to manage posts.List Customization:
protected function configureListFields(ListMapper $list): void
{
$list->add('title', 'string', ['template' => 'admin/list__title.html.twig']);
$list->add('createdAt', 'datetime', ['format' => 'd/m/Y H:i']);
}
Use templates for complex rendering (e.g., nested objects, icons).
Form Customization:
protected function configureFormFields(FormMapper $form): void
{
$form->add('title', 'text', ['label' => 'Post Title']);
$form->add('content', 'sonata_media_type', ['context' => 'default']);
$form->add('tags', 'sonata_type_model', ['class' => Tag::class, 'multiple' => true]);
}
Leverage Sonata’s form types (e.g., sonata_media_type, sonata_type_model).
Datagrid Filters:
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter->add('title', null, ['label' => 'Search by title']);
$filter->add('published', 'doctrine_boolean', ['label' => 'Published']);
}
# config/packages/security.yaml
access_control:
- { path: ^/admin/, roles: ROLE_ADMIN }
public function isAccessGranted(): bool
{
return $this->getSubject()->hasRole('ROLE_SUPER_ADMIN');
}
protected function configureListFields(ListMapper $list): void
{
$list->add('_action', 'actions', [
'actions' => [
'delete' => [],
'export' => ['template' => 'SonataAdminBundle:CRUD:list__action_export.html.twig'],
]
]);
}
Extend batch actions via configureBatchActions().
protected function configureFormFields(FormMapper $form): void
{
$form->add('image', 'sonata_media_type', [
'context' => 'default',
'provider' => 'sonata.media.provider.file',
]);
}
Requires sonata-project/media-bundle and proper media provider setup.
templates/admin/:
templates/
admin/
App/
User/
list.html.twig # Override list view
edit.html.twig # Override edit form
Doctrine Extensions:
Use sonata-project/doctrine-extensions for soft deletes, timestamps, etc.
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class User { ... }
Event Listeners: Hook into lifecycle events:
public function prePersist($object): void
{
$object->setCreatedAt(new \DateTime());
}
API Integration:
Use sonata-project/admin-bundle's serialize method to expose data:
public function serialize($object): array
{
return [
'id' => $object->getId(),
'title' => $object->getTitle(),
];
}
Translation:
# config/packages/sonata_admin.yaml
sonata_admin:
options:
translation_domain: 'admin'
Add translations in translations/admin.en.yaml.
Route Conflicts:
Sonata’s routes may clash with custom routes. Use _sonata_admin prefix or exclude routes:
# config/routes.yaml
sonata_admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin
options:
expose: true
Cache Issues: Clear cache after changes:
php bin/console cache:clear
php bin/console sonata:admin:cache:clear
Doctrine Proxy Classes:
Sonata may fail with proxies. Ensure proxy_dir is configured in Doctrine:
# config/packages/doctrine.yaml
doctrine:
orm:
entity_managers:
default:
metadata_cache_driver: apcu
query_cache_driver: apcu
result_cache_driver: apcu
proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
Form Type Conflicts: Avoid naming conflicts with Sonata’s form types. Prefix custom types:
$form->add('custom_field', 'app_custom_type');
JavaScript/CSS Assets: Sonata relies on Webpack Encore or AssetMapper. Ensure assets are built:
npm run dev
Enable Debug Mode:
# config/packages/dev/sonata_admin.yaml
sonata_admin:
debug: true
Log SQL Queries:
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
logging_format: '%%timestamp%% %%sql%%'
logging_connection: default
Check Admin Configuration:
Use the sonata:admin:dump command to validate admin classes:
php bin/console sonata:admin:dump
Override Templates for Debugging: Create a custom template to inspect variables:
{# templates/admin/App/User/list.html.twig #}
<pre>{{ dump(admin.list) }}</pre>
Custom Admin Classes:
Extend AbstractAdmin for reusable logic:
abstract class BaseAdmin extends AbstractAdmin
{
protected function configureCommonFields(FormMapper $form): void
{
$form->add('createdAt', 'datetime', ['disabled' => true]);
}
}
Dynamic Fields:
Use configureFormFields dynamically:
protected function configureFormFields(FormMapper $form): void
{
$form->add('dynamic_field', 'text', [
'
How can I help you explore Laravel packages today?