dgarden/sonata-attribute-bundle
Installation
Run composer require dgarden/sonata-admin-attribute-bundle and ensure the bundle is enabled in config/bundles.php:
DigitalGarden\SonataAttributeBundle\DigitalGardenSonataAttributeBundle::class => ['all' => true],
First Use Case
Add the [Admin] attribute to an entity class (e.g., Bank). Start with a basic configuration:
use DigitalGarden\SonataAttributeBundle\Attribute\Admin;
#[Admin(fields: ['name' => new AdminAttribute('name')])]
class Bank {}
Clear the cache (php bin/console cache:clear) and visit /admin to see the generated admin interface.
Where to Look First
[Admin] attributes.config/packages/digital_garden_sonata_attribute.yaml for global overrides (if any).Attribute-Based Admin Generation Replace or supplement Sonata’s YAML/XML configuration with PHP attributes. Example:
#[Admin(
fields: [
'name' => new AdminAttribute('name', ['label' => 'Bank Name']),
'createdAt' => new AdminAttribute('createdAt', ['type' => 'datetime']),
],
list: [
new AdminAttribute(ListMapper::NAME_ID, ['type' => ListMapper::TYPE_ID]),
'name' => new AdminAttribute('name'),
],
form: [
'name' => new AdminAttribute('name', ['required' => true]),
]
)]
class Bank {}
Integration with Existing Sonata Admins Extend existing Sonata admin classes by combining attributes with traditional configuration:
#[Admin(
list: [
new AdminAttribute(ListMapper::NAME_ACTIONS, [
'type' => ListMapper::TYPE_ACTIONS,
'actions' => ['delete' => ['template' => '@App/Admin/delete.html.twig']],
]),
]
)]
class Bank extends AbstractAdmin {}
Dynamic Attribute Generation Use runtime logic to build attributes dynamically (e.g., for polymorphic entities):
#[Admin(
fields: function (Bank $entity) {
return [
'name' => new AdminAttribute('name'),
'isActive' => new AdminAttribute('isActive', [
'editable' => $entity->isAdmin(),
]),
];
}
)]
Reusing Attributes Across Entities Create a base trait or abstract class to share common admin configurations:
trait CommonAdminConfig {
public static function getCommonAdminAttributes(): array {
return [
'createdAt' => new AdminAttribute('createdAt', ['type' => 'datetime']),
];
}
}
#[Admin(fields: [...CommonAdminConfig::getCommonAdminAttributes()])]
class Bank {}
Event Listeners for Post-Processing Hook into Sonata’s events to modify attributes dynamically:
// src/EventListener/AdminAttributeListener.php
public function onConfigureListFields(ListMapperEvent $event) {
$attributes = $event->getAdmin()->getAttributes();
if (isset($attributes['customField'])) {
$attributes['customField']->setOption('label', 'Dynamic Label');
}
}
Cache Dependencies
php bin/console cache:clear --env=prod
#[Cache(clear: true)] on entities to auto-clear cache on changes (if using Doctrine extensions).Attribute Overrides
#[Admin(override: true)].Type Safety
AdminAttribute types (e.g., ListMapper::TYPE_BATCH for a non-batch field) may cause runtime errors.Doctrine Metadata Conflicts
#[ORM\Entity] and #[ORM\Table] are correctly defined before [Admin].Symfony 6+ Deprecations
#[AsAdmin] vs. #[Admin]).composer.json for Symfony version constraints and test thoroughly.Dumping Attributes Use Sonata’s debug commands to inspect generated admins:
php bin/console sonata:admin:debug
Or dump attributes in a controller:
$admin = $this->get('sonata.admin.bank');
dump($admin->getAttributes());
Twig Debugging Enable Sonata’s debug toolbar to inspect rendered admin templates:
# config/packages/dev/sonata_admin.yaml
sonata_admin:
templates:
layout: 'SonataAdminBundle::standard_layout.html.twig'
debug: true
Event Debugging Log events to trace attribute processing:
public function onConfigureListFields(ListMapperEvent $event) {
$this->logger->debug('Configuring list fields', ['admin' => $event->getAdmin()->getClass()]);
}
Gradual Migration Start by converting simple admins to attributes, then migrate complex ones incrementally. Use both YAML and attributes in parallel during transition:
# config/packages/sonata_admin.yaml
sonata_admin:
options:
attribute_bundle: true # Enable attribute bundle
Custom Attribute Classes
Extend AdminAttribute to add domain-specific options:
class CustomAdminAttribute extends AdminAttribute {
public function __construct(string $property, array $options = []) {
parent::__construct($property, $options + ['custom_option' => true]);
}
}
Performance
#[Admin(fields: [])] to exclude fields from admin generation.#[Admin(fields: static::getCachedAttributes())]
class Bank {}
Testing
Use Symfony’s KernelTestCase to test attribute-based admins:
public function testAdminAttributes() {
$client = static::createClient();
$client->request('GET', '/admin/app/bank');
$this->assertSelectorTextContains('h1', 'Banks');
}
Documentation Gaps
How can I help you explore Laravel packages today?