Some icons have been renamed by Font Awesome, and the correct class is now fas or fab instead of
fa, for example before you would have used fa fa-edit, and after fas fa-edit.
Sonata includes the compatibility layer of Font Awesome 5 that ensure the old icons names from version 4 still works. So we encourage to upgrade the names of all your custom icons, but the old code should still work.
If you still need it, please set it up on your own!
Frontend dependencies are handled with NPM. Bower is not used anymore.
A lot of assets that were previously public are handled with NPM and placed in a private node_modules/ directory.
From these dependencies, only the necessary files are exposed publicly through Webpack Encore.
The jQuery UI dependency was fully included before, but now we only include the sortable widget (JavaScript and CSS), the rest of this dependency is not exposed. If you are adding more JavaScript or CSS using another widget of jQuery UI please include it yourself.
Please check the src/Resources/public and the documentation to see the used CSS, JavaScript, images and fonts.
If you are customising (specially removing standard JavaScript or CSS) assets, this will affect you.
All the deprecated code introduced on 3.x is removed on 4.0.
Please read 3.x upgrade guides for more information.
See also the diff code.
Some classes and methods are now final and should not be overridden:
Sonata\AdminBundle\Admin\AbstractAdmin::getActionButtonsSonata\AdminBundle\Admin\AbstractAdmin::getBatchActionsSonata\AdminBundle\Admin\AbstractAdmin::urlizeSonata\AdminBundle\Admin\Extension\LockExtensionSonata\AdminBundle\Admin\FieldDescriptionCollectionSonata\AdminBundle\Block\AdminListBlockServiceSonata\AdminBundle\Block\AdminSearchBlockServiceSonata\AdminBundle\Block\AdminStatsBlockServiceSonata\AdminBundle\Command\ExplainAdminCommandSonata\AdminBundle\Command\GenerateObjectAclCommandSonata\AdminBundle\Command\ListAdminCommandSonata\AdminBundle\Command\SetupAclCommandSonata\AdminBundle\Command\ValidatorsSonata\AdminBundle\Datagrid\DatagridSonata\AdminBundle\Datagrid\SimplePagerSonata\AdminBundle\DependencyInjection\Compiler\AddDependencyCallsCompilerPassSonata\AdminBundle\DependencyInjection\Compiler\AddFilterTypeCompilerPassSonata\AdminBundle\DependencyInjection\Compiler\ExtensionCompilerPassSonata\AdminBundle\DependencyInjection\Compiler\GlobalVariablesCompilerPassSonata\AdminBundle\DependencyInjection\ConfigurationSonata\AdminBundle\DependencyInjection\SonataAdminExtensionSonata\AdminBundle\DependencyInjection\Compiler\ModelManagerCompilerPassSonata\AdminBundle\Event\AdminEventExtensionSonata\AdminBundle\Event\ConfigureEventSonata\AdminBundle\Event\ConfigureMenuEventSonata\AdminBundle\Event\ConfigureQueryEventSonata\AdminBundle\Event\PersistenceEventSonata\AdminBundle\Exception\LockExceptionSonata\AdminBundle\Exception\ModelManagerExceptionSonata\AdminBundle\Exception\NoValueExceptionSonata\AdminBundle\Filter\FilterFactorySonata\AdminBundle\Form\ChoiceList\ModelChoiceLoaderSonata\AdminBundle\Form\DataTransformer\ArrayToModelTransformerSonata\AdminBundle\Form\DataTransformer\ModelToIdPropertyTransformerSonata\AdminBundle\Form\DataTransformer\ModelToIdTransformerSonata\AdminBundle\Form\DataTransformer\ModelsToArrayTransformerSonata\AdminBundle\Form\EventListener\MergeCollectionListenerSonata\AdminBundle\Form\Extension\ChoiceTypeExtensionSonata\AdminBundle\Form\Extension\Field\Type\FormTypeFieldExtensionSonata\AdminBundle\Form\Extension\Field\Type\MopaCompatibilityTypeFieldExtensionSonata\AdminBundle\Form\Type\AclMatrixTypeSonata\AdminBundle\Form\Type\AdminTypeSonata\AdminBundle\Form\Type\ChoiceFieldMaskTypeSonata\AdminBundle\Form\Type\CollectionTypeSonata\AdminBundle\Form\Type\Filter\ChoiceTypeSonata\AdminBundle\Form\Type\Filter\DateRangeTypeSonata\AdminBundle\Form\Type\Filter\DateTimeRangeTypeSonata\AdminBundle\Form\Type\Filter\DateTimeTypeSonata\AdminBundle\Form\Type\Filter\DateTypeSonata\AdminBundle\Form\Type\Filter\DefaultTypeSonata\AdminBundle\Form\Type\Filter\NumberTypeSonata\AdminBundle\Form\Type\ModelAutocompleteTypeSonata\AdminBundle\Form\Type\ModelHiddenTypeSonata\AdminBundle\Form\Type\ModelListTypeSonata\AdminBundle\Form\Type\ModelReferenceTypeSonata\AdminBundle\Form\Type\ModelTypeSonata\AdminBundle\Guesser\TypeGuesserChainSonata\AdminBundle\Manipulator\ServicesManipulatorSonata\AdminBundle\Menu\MenuBuilderSonata\AdminBundle\Model\AuditManagerSonata\AdminBundle\Route\AdminPoolLoaderSonata\AdminBundle\Route\DefaultRouteGeneratorSonata\AdminBundle\Route\RouteCollectionSonata\AdminBundle\Route\RoutesCacheSonata\AdminBundle\Route\RoutesCacheWarmUpSonata\AdminBundle\Security\Acl\AdminPermissionMapSonata\AdminBundle\Security\Acl\Permission\MaskBuilderSonata\AdminBundle\Security\Handler\AclSecurityHandlerSonata\AdminBundle\Security\Handler\NoopSecurityHandlerSonata\AdminBundle\SonataAdminBundleSonata\AdminBundle\Translator\BCLabelTranslatorStrategySonata\AdminBundle\Translator\FormLabelTranslatorStrategySonata\AdminBundle\Translator\NativeLabelTranslatorStrategySonata\AdminBundle\Translator\UnderscoreLabelTranslatorStrategySonata\AdminBundle\Twig\Extension\SonataAdminExtensionSonata\AdminBundle\Twig\GlobalVariablesSonata\AdminBundle\Util\AdminAclManipulatorSonata\AdminBundle\Util\FormBuilderIteratorSonata\AdminBundle\Util\FormViewIteratorSonata\AdminBundle\Util\AdminAclManipulator
If you have implemented a custom admin, you must adapt the signature of the following new methods to match the one in AdminInterface again:
hasAccessconfigureActionButtonsgetExportFieldssetTemplatessetTemplategetTemplatesgetClassnameLabelgetPersistentParameterpreValidategetSubClassesaddSubClassgetDashboardActionsgetActionButtonsisCurrentRoute
The following methods changed their visiblity to protected:
configureActionButtonsconfigureurlize
The method signature of configureActionButtons has changed. A new parameter buttonList was added.
Some of the tasks to update your admin classes can be automated using Rector.
Use the below config in your rector.php config as a starting point to automate your update.
<?php
declare(strict_types=1);
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StringType;
use PHPStan\Type\UnionType;
use PHPStan\Type\VoidType;
use Rector\Config\RectorConfig;
use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
use Rector\TypeDeclaration\ValueObject\AddParamTypeDeclaration;
use Rector\TypeDeclaration\ValueObject\AddReturnTypeDeclaration;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
return static function (RectorConfig $rectorConfig): void {
// ....
$services = $rectorConfig->services();
$rectorConfig->ruleWithConfiguration(AddReturnTypeDeclarationRector::class, [
new AddReturnTypeDeclaration(AbstractAdmin::class, 'preBatchAction', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configure', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'alterNewInstance', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'alterObject', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'preValidate', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'preUpdate', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'postUpdate', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'prePersist', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'postPersist', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'preRemove', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'postRemove', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureFormFields', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureListFields', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureDatagridFilters', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureShowFields', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureRoutes', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureTabMenu', new VoidType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'generateBaseRoutePattern', new StringType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'generateBaseRouteName', new StringType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'getSubClass', new StringType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'createNewInstance', new ObjectWithoutClassType()),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configurePersistentParameters', new ArrayType(new StringType(), new MixedType())),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureFilterParameters', new ArrayType(new StringType(), new MixedType())),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureActionButtons', new ArrayType(new StringType(), new MixedType())),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureExportFields', new ArrayType(new IntegerType(), new StringType())),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureDashboardActions', new ArrayType(new StringType(), new ArrayType(new StringType(), new MixedType()))),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureBatchActions', new ArrayType(new StringType(), new ArrayType(new StringType(), new MixedType()))),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'getAccessMapping', new ArrayType(new StringType(), new UnionType([new StringType(), new ArrayType(new IntegerType(), new StringType())]))),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'getPermissionsShow', new ArrayType(new IntegerType(), new StringType())),
new AddReturnTypeDeclaration(AbstractAdmin::class, 'configureQuery', new ObjectType(ProxyQueryInterface::class)),
]);
$rectorConfig->ruleWithConfiguration(AddParamTypeDeclarationRector::class, [
new AddParamTypeDeclaration(AbstractAdmin::class, 'alterNewInstance', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'alterObject', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'preValidate', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'preUpdate', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'postUpdate', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'prePersist', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'postPersist', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'preRemove', 0, new ObjectWithoutClassType()),
new AddParamTypeDeclaration(AbstractAdmin::class, 'postRemove', 0, new ObjectWithoutClassType()),
]);
$services->set(AddParamBasedOnParentClassMethodRector::class);
};If you have implemented a custom admin extension, you must adapt the signature of the following new methods to match the one in AdminExtensionInterface again:
configureActionButtonsconfigureBatchActionsgetAccessMapping
The third argument of the AdminListBlockService::__construct method is now mandatory.
The fourth argument of the AdminListBlockService::__construct method is now mandatory.
The AdminHelper::__construct method changes its Pool param to a PropertyAccessorInterface one.
The buildBreadcrumbs method may no longer be called from outside the class.
The buildBreadcrumbs method has been removed from the interface.
The Twig filters that come with the bundle will no longer load a default template when used with a missing template.
The sonata_admin twig extension is now final. You may no longer extend it.
Method SimplePager::getResults is always returning an array
LockInterface extends from ModelManagerInterface.
RouteCollection implements RouteCollectionInterface.
When there is no searchable filters, SearchHandler::search() returns null. Previously, it was returning false.
When the service security.csrf.token_manager is not available, getCsrfToken() returns null. Previously, it was returning false.
The isXmlHttpRequest(), redirectTo(), isPreviewApproved(), isInPreviewMode(), isPreviewDeclined(),
validateCsrfToken() signatures were changed. They now require to pass the request as first argument.
The CRUDController::getRequest() method was removed.
The type for argument 4 in apply() method has been changed from array to Sonata\AdminBundle\Filter\Model\FilterData.
Before:
public function apply(ProxyQueryInterface $query, array $filterData): void;After:
public function apply(ProxyQueryInterface $query, FilterData $filterData): void;The form label are now correctly using the label translator strategy for field with .
(which won't be replaced by __). For instance, with the underscore label strategy, the
label foo.barBaz was previously form.label_foo__bar_baz and now is form.label_foo_bar_baz
to be consistent with others labels like show.label_foo_bar_baz.
They don't reset the existing templates anymore.
BaseFieldDescription, FieldDescriptionCollection, FieldDescriptionInterface and FieldDescriptionRegistryInterface
Moved from the Sonata\AdminBundle\Admin to the Sonata\AdminBundle\FieldDescription namespace.
Remove AdminInterface $admin argument from
BuilderInterface::fixFieldDescription()DatagridBuilderInterface::addFilter()ListBuilderInterface::buildField()ListBuilderInterface::addField()ShowBuilderInterface::addField()
Use $fieldDescription->getAdmin() to access to the admin value.
ListMapper::NAME_ACTIONS change to _actions.
ListMapper::NAME_BATCH change to _batch.
ListMapper::NAME_SELECT change to _select.
Be aware it implies that the following code
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('_action', null, [
'actions' => [
'show' => [],
'edit' => [],
'delete' => [],
]
]);
}should be updated to
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('_actions', null, [
'actions' => [
'show' => [],
'edit' => [],
'delete' => [],
]
]);
}but the best is to use the constant ListMapper::NAME_ACTIONS.
Instead of relying on the ROLE_MYADMIN_EDIT role, a new ROLE_MYADMIN_HISTORY
role was introduced to get access to the history actions. If you use the
revisions be sure to add this role to your users.