sonata-project/easy-extends-bundle
Installation:
composer require sonata-project/easy-extends-bundle
Add to config/bundles.php:
return [
// ...
SonataEasyExtendsBundle\SonataEasyExtendsBundle::class => ['all' => true],
];
Enable in config/packages/sonata_easy_extends.yaml:
sonata_easy_extends:
enabled: true
# Optional: Customize template paths
templates:
base: 'SonataEasyExtendsBundle::base.html.twig'
First Use Case:
Extend a Sonata admin controller (e.g., ProductAdmin) by creating a custom admin class:
// src/Admin/ProductAdminExtension.php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdminExtension;
use Sonata\AdminBundle\Form\FormMapper;
class ProductAdminExtension extends AbstractAdminExtension
{
public function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('custom_field', 'text');
}
}
Register the extension in services.yaml:
services:
App\Admin\ProductAdminExtension:
tags:
- { name: sonata.admin.extension, manager_type: orm, group: product, label: product }
Extending Admin Controllers:
AbstractAdminExtension to add fields, actions, or modify behavior.configureFormFields(), configureListFields(), or prePersist().Dynamic Extensions: Load extensions conditionally (e.g., based on user roles or features):
public function isEnabled()
{
return $this->getConfigurationPool()->getContainer()->get('security.authorization_checker')->isGranted('ROLE_ADMIN');
}
Template Overrides: Extend Sonata templates by overriding blocks in your custom templates:
{# templates/admin/product_edit.html.twig #}
{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}
{% block sonata_admin_content %}
{{ parent() }}
<div class="custom-block">
<!-- Custom content -->
</div>
{% endblock %}
Event Listeners:
Attach listeners to Sonata events (e.g., sonata.admin.event.configure) for runtime modifications:
// src/EventListener/AdminListener.php
use Sonata\AdminBundle\Event\ConfigureEvent;
class AdminListener
{
public function onConfigure(ConfigureEvent $event)
{
$event->getAdminPool()->getAdminByAdminCode('product')->addField('custom_field');
}
}
Register in services.yaml:
services:
App\EventListener\AdminListener:
tags:
- { name: kernel.event_listener, event: sonata.admin.event.configure, method: onConfigure }
Dependency Injection: Inject services into extensions for reusable logic:
public function __construct(private CustomService $customService) {}
public function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('dynamic_field', 'text', [
'data' => $this->customService->getDynamicValue(),
]);
}
sonata-project/admin-bundle (v3.x or v4.x).manager_type: mongo.sonata-project/admin-bundle is installed first.AdminExtension in PHPUnit tests:
$extension = $this->createMock(AbstractAdminExtension::class);
$extension->method('isEnabled')->willReturn(true);
Extension Loading Order: Extensions are loaded alphabetically by class name. Use explicit tags to control order:
tags:
- { name: sonata.admin.extension, priority: 100 } # Higher priority loads first
Caching Issues: Clear cache after adding new extensions:
php bin/console cache:clear
Deprecated Methods:
Avoid using deprecated methods (e.g., configure() in favor of configureFormFields()).
Namespace Conflicts:
Ensure extension class namespaces are unique (e.g., App\Admin\ProductAdminExtension).
Archived Bundle: No active maintenance; use at your own risk. Fork if critical fixes are needed.
Check Extension Registration: Verify extensions are loaded via:
php bin/console debug:container sonata.admin.extension
Log Extension Events: Enable debug mode to log extension-related events:
# config/packages/dev/monolog.yaml
monolog:
handlers:
sonata:
type: stream
path: "%kernel.logs_dir%/sonata.log"
level: debug
Template Debugging:
Use Twig’s {% debug %} to inspect variables in overridden templates.
Reuse Extensions:
Create base extensions for common functionality (e.g., SoftDeleteExtension) and extend them:
class ProductAdminExtension extends SoftDeleteExtension
{
public function configureFormFields(FormMapper $formMapper)
{
parent::configureFormFields($formMapper);
$formMapper->add('sku', 'text');
}
}
Dynamic Field Mapping:
Use add() with dynamic options:
$formMapper->add('price', 'money', [
'currency' => $this->getConfigurationPool()->getContainer()->getParameter('app.currency'),
]);
Validation: Add validation rules in extensions:
public function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', 'text', [
'constraints' => [
new NotBlank(),
new Length(['min' => 3]),
],
]);
}
Performance: Avoid heavy logic in extensions. Offload to services where possible.
Documentation: Since the bundle is archived, refer to:
How can I help you explore Laravel packages today?