From 79824c6cd7cd5a04ae0d44db8f2dabcc9ced46a8 Mon Sep 17 00:00:00 2001 From: simbr Date: Thu, 5 Oct 2023 09:14:04 +0200 Subject: [PATCH] Sonata 4 compatibility --- Admin/ContentAdmin.php | 46 ++--- Admin/Extension/PageExtension.php | 2 +- Admin/FormContractor.php | 219 +++++++++++++++++++++ Admin/PageAdmin.php | 39 +--- Admin/PageTypeAdmin.php | 20 +- DependencyInjection/Compiler/AdminPass.php | 32 ++- Resources/config/services.yml | 27 ++- SherlockodeSonataAdvancedContentBundle.php | 3 +- 8 files changed, 299 insertions(+), 89 deletions(-) create mode 100644 Admin/FormContractor.php diff --git a/Admin/ContentAdmin.php b/Admin/ContentAdmin.php index b42f345..26e913e 100644 --- a/Admin/ContentAdmin.php +++ b/Admin/ContentAdmin.php @@ -6,6 +6,7 @@ use Sherlockode\AdvancedContentBundle\Model\ContentInterface; use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Datagrid\ListMapper; +use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; use Sonata\AdminBundle\Form\FormMapper; use Symfony\Component\Form\FormBuilderInterface; @@ -13,15 +14,15 @@ class ContentAdmin extends AbstractAdmin { protected $baseRouteName = 'admin_afb_content'; - public function getFormTheme() + protected function configure(): void { - return array_merge( + $this->setFormTheme(array_merge( parent::getFormTheme(), ['@SherlockodeAdvancedContent/Form/content.html.twig'] - ); + )); } - public function configureFormFields(FormMapper $form) + public function configureFormFields(FormMapper $form): void { $form->tab('content.form.tabs.label') ->with('content.form.tabs.general', [ @@ -42,31 +43,7 @@ public function configureFormFields(FormMapper $form) ->end(); // init the groups data for this admin class } - /** - * Create ContentType form - * - * @return FormBuilderInterface - * - * @throws \Exception - */ - private function getCustomFormBuilder() - { - return $this->getFormContractor() - ->getFormFactory() - ->createNamedBuilder($this->getUniqid(), ContentType::class, null, $this->formOptions); - } - - public function getFormBuilder() - { - $this->formOptions['data_class'] = $this->getClass(); - - $formBuilder = $this->getCustomFormBuilder(); - $this->defineFormBuilder($formBuilder); - - return $formBuilder; - } - - public function configureListFields(ListMapper $list) + public function configureListFields(ListMapper $list): void { $list ->add('id', null, ['label' => 'content.id']) @@ -80,9 +57,14 @@ public function configureListFields(ListMapper $list) ; } - public function createQuery($context = 'list') + /** + * @param ProxyQueryInterface $query + * + * @return ProxyQueryInterface + */ + public function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface { - $query = parent::createQuery(); + $query = parent::configureQuery($query); $query->getQueryBuilder() ->where('o.page IS NULL'); @@ -94,7 +76,7 @@ public function createQuery($context = 'list') * * @return string */ - public function toString($object) + public function toString($object): string { if ($object instanceof ContentInterface && $object->getName()) { return $object->getName(); diff --git a/Admin/Extension/PageExtension.php b/Admin/Extension/PageExtension.php index 6536ec5..1125e77 100644 --- a/Admin/Extension/PageExtension.php +++ b/Admin/Extension/PageExtension.php @@ -15,7 +15,7 @@ class PageExtension extends AbstractAdminExtension private $configurationManager; - public function configureFormFields(FormMapper $formMapper) + public function configureFormFields(FormMapper $formMapper): void { if ($formMapper->has('pageType')) { $formMapper->tab('page.form.tabs.label') diff --git a/Admin/FormContractor.php b/Admin/FormContractor.php new file mode 100644 index 0000000..a710533 --- /dev/null +++ b/Admin/FormContractor.php @@ -0,0 +1,219 @@ +formFactory = $formFactory; + $this->formRegistry = $formRegistry; + } + + /** + * @param FieldDescriptionInterface $fieldDescription + * + * @return void + */ + public function fixFieldDescription(FieldDescriptionInterface $fieldDescription): void + { + $fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'standard')); + + if ($fieldDescription->describesAssociation() || null !== $fieldDescription->getOption('admin_code')) { + $fieldDescription->getAdmin()->attachAdminClass($fieldDescription); + } + } + + /** + * @param string $name + * @param array $formOptions + * + * @return FormBuilderInterface + */ + public function getFormBuilder(string $name, array $formOptions = []): FormBuilderInterface + { + return $this->formFactory->createNamedBuilder($name, $this->formType, null, $formOptions); + } + + /** + * @param string $formType + * + * @return $this + */ + public function setFormType(string $formType): self + { + $this->formType = $formType; + + return $this; + } + + /** + * @param string|null $type + * @param FieldDescriptionInterface $fieldDescription + * @param array $formOptions + * + * @return array|mixed[] + */ + final public function getDefaultOptions( + ?string $type, + FieldDescriptionInterface $fieldDescription, + array $formOptions = [] + ): array { + $options = []; + $options['sonata_field_description'] = $fieldDescription; + + if ($this->isAnyInstanceOf($type, [ + ModelType::class, + ModelListType::class, + ModelHiddenType::class, + ModelAutocompleteType::class, + ModelReferenceType::class, + ])) { + $options['class'] = $fieldDescription->getTargetModel(); + $options['model_manager'] = $fieldDescription->getAdmin()->getModelManager(); + + if ($this->isAnyInstanceOf($type, [ModelAutocompleteType::class])) { + if (!$fieldDescription->hasAssociationAdmin()) { + throw new \InvalidArgumentException(sprintf( + 'The current field `%s` is not linked to an admin.' + .' Please create one for the target model: `%s`.', + $fieldDescription->getName(), + $fieldDescription->getTargetModel() ?? '' + )); + } + } + } elseif ($this->isAnyInstanceOf($type, [AdminType::class])) { + if (!$fieldDescription->hasAssociationAdmin()) { + throw new \InvalidArgumentException(sprintf( + 'The current field `%s` is not linked to an admin.' + .' Please create one for the target model: `%s`.', + $fieldDescription->getName(), + $fieldDescription->getTargetModel() ?? '' + )); + } + + if (!$fieldDescription->describesSingleValuedAssociation()) { + throw new \InvalidArgumentException(sprintf( + 'You are trying to add `%s` field `%s` which is not a One-To-One or Many-To-One association.' + .' You SHOULD use `%s` instead.', + AdminType::class, + $fieldDescription->getName(), + CollectionType::class + )); + } + + // set sensitive default value to have a component working fine out of the box + $options['btn_add'] = false; + $options['delete'] = false; + + $options['data_class'] = $fieldDescription->getAssociationAdmin()->getClass(); + $options['empty_data'] = static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance(); + $fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'admin')); + } elseif ($this->isAnyInstanceOf($type, [ + CollectionType::class, + ])) { + if (!$fieldDescription->hasAssociationAdmin()) { + throw new \InvalidArgumentException(sprintf( + 'The current field `%s` is not linked to an admin.' + .' Please create one for the target model: `%s`.', + $fieldDescription->getName(), + $fieldDescription->getTargetModel() ?? '' + )); + } + + $options['type'] = AdminType::class; + $options['modifiable'] = true; + $options['type_options'] = $this->getDefaultAdminTypeOptions($fieldDescription, $formOptions); + } + + return $options; + } + + /** + * @param string|null $type + * @param array $classes + * + * @return bool + */ + private function isAnyInstanceOf(?string $type, array $classes): bool + { + if (null === $type) { + return false; + } + + foreach ($classes as $class) { + if (is_a($type, $class, true)) { + return true; + } + } + + // handle form type inheritance and check all parent types + $resolvedType = $this->formRegistry->getType($type); + $parentType = $resolvedType->getParent(); + if (null !== $parentType) { + $parentType = \get_class($parentType->getInnerType()); + + // all types have "Symfony\Component\Form\Extension\Core\Type\FormType" as parent + // so we ignore it here for performance reasons + if (FormType::class !== $parentType) { + return $this->isAnyInstanceOf($parentType, $classes); + } + } + + return false; + } + + /** + * @param FieldDescriptionInterface $fieldDescription + * @param array $formOptions + * + * @return array + */ + private function getDefaultAdminTypeOptions(FieldDescriptionInterface $fieldDescription, array $formOptions): array + { + $typeOptions = [ + 'sonata_field_description' => $fieldDescription, + 'data_class' => $fieldDescription->getAssociationAdmin()->getClass(), + 'empty_data' => static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance(), + ]; + + if (isset($formOptions['by_reference'])) { + $typeOptions['collection_by_reference'] = $formOptions['by_reference']; + } + + return $typeOptions; + } +} diff --git a/Admin/PageAdmin.php b/Admin/PageAdmin.php index 6269cf0..9e45913 100644 --- a/Admin/PageAdmin.php +++ b/Admin/PageAdmin.php @@ -17,15 +17,15 @@ class PageAdmin extends AbstractAdmin */ private $localeProvider; - public function getFormTheme() + protected function configure(): void { - return array_merge( + $this->setFormTheme(array_merge( parent::getFormTheme(), ['@SherlockodeAdvancedContent/Form/content.html.twig'] - ); + )); } - public function configureFormFields(FormMapper $form) + public function configureFormFields(FormMapper $form): void { $form->tab('page.form.tabs.label') ->with('page.form.tabs.general')->end(); // init the groups data for this admin class @@ -69,37 +69,14 @@ public function configureFormFields(FormMapper $form) $form->end(); } - public function getFormBuilder() - { - $this->formOptions['data_class'] = $this->getClass(); - - $formBuilder = $this->getCustomFormBuilder(); - $this->defineFormBuilder($formBuilder); - - return $formBuilder; - } - - /** - * Create PageType form - * - * @return FormBuilderInterface - * - * @throws \Exception - */ - private function getCustomFormBuilder() - { - return $this->getFormContractor() - ->getFormFactory() - ->createNamedBuilder($this->getUniqid(), PageType::class, null, $this->formOptions); - } - - public function configureListFields(ListMapper $list) + public function configureListFields(ListMapper $list): void { $list ->add('pageIdentifier', null, ['label' => 'page.form.page_identifier']) ->add('title', null, [ 'label' => 'page.form.title', - 'template' => '@SherlockodeSonataAdvancedContent/Page/title.html.twig' + 'template' => '@SherlockodeSonataAdvancedContent/Page/title.html.twig', + 'virtual_field' => true, ]) ->add('status', null, [ 'label' => 'page.form.status', @@ -119,7 +96,7 @@ public function configureListFields(ListMapper $list) * * @return string */ - public function toString($object) + public function toString($object): string { if ($object instanceof PageInterface && $object->getPageIdentifier()) { return $object->getPageIdentifier(); diff --git a/Admin/PageTypeAdmin.php b/Admin/PageTypeAdmin.php index c67c288..180d85f 100644 --- a/Admin/PageTypeAdmin.php +++ b/Admin/PageTypeAdmin.php @@ -11,7 +11,7 @@ class PageTypeAdmin extends AbstractAdmin { - public function configureFormFields(FormMapper $form) + public function configureFormFields(FormMapper $form): void { $form ->add('name') @@ -24,21 +24,7 @@ private function getPageTypeFormBuilder() ->createNamedBuilder($this->getUniqid(), PageTypeType::class); } - public function getFormBuilder() - { - $formBuilder = $this->getPageTypeFormBuilder(); - $this->defineFormBuilder($formBuilder); - - return $formBuilder; - } - - public function defineFormBuilder(FormBuilderInterface $formBuilder) - { - $formBuilder = $this->getPageTypeFormBuilder(); - parent::defineFormBuilder($formBuilder); - } - - public function configureListFields(ListMapper $list) + public function configureListFields(ListMapper $list): void { $list ->add('name', null, ['label' => 'page_type.form.name']) @@ -56,7 +42,7 @@ public function configureListFields(ListMapper $list) * * @return string */ - public function toString($object) + public function toString($object): string { if ($object instanceof PageTypeInterface && $object->getName()) { return $object->getName(); diff --git a/DependencyInjection/Compiler/AdminPass.php b/DependencyInjection/Compiler/AdminPass.php index d5a2704..c544a10 100644 --- a/DependencyInjection/Compiler/AdminPass.php +++ b/DependencyInjection/Compiler/AdminPass.php @@ -11,11 +11,31 @@ public function process(ContainerBuilder $container) { $mapping = $container->getParameter('sherlockode_advanced_content.entity_class_mapping'); - $container->getDefinition('sherlockode_advanced_content.admin.content') - ->replaceArgument(1, $mapping['content']); - $container->getDefinition('sherlockode_advanced_content.admin.page_type') - ->replaceArgument(1, $mapping['page_type']); - $container->getDefinition('sherlockode_advanced_content.admin.page') - ->replaceArgument(1, $mapping['page']); + $tag = $container->getDefinition('sherlockode_advanced_content.admin.content')->getTag('sonata.admin'); + $tag = $tag[0] + ['model_class' => $mapping['content']]; + + $container + ->getDefinition('sherlockode_advanced_content.admin.content') + ->setTags(['sonata.admin' => [$tag]]) + ->addMethodCall('setFormContractor', [$container->getDefinition('sherlockode_advanced_content.form_contractor.content')]) + ; + + $tag = $container->getDefinition('sherlockode_advanced_content.admin.page')->getTag('sonata.admin'); + $tag = $tag[0] + ['model_class' => $mapping['page']]; + + $container + ->getDefinition('sherlockode_advanced_content.admin.page') + ->setTags(['sonata.admin' => [$tag]]) + ->addMethodCall('setFormContractor', [$container->getDefinition('sherlockode_advanced_content.form_contractor.page')]) + ; + + $tag = $container->getDefinition('sherlockode_advanced_content.admin.page_type')->getTag('sonata.admin'); + $tag = $tag[0] + ['model_class' => $mapping['page_type']]; + + $container + ->getDefinition('sherlockode_advanced_content.admin.page_type') + ->setTags(['sonata.admin' => [$tag]]) + ->addMethodCall('setFormContractor', [$container->getDefinition('sherlockode_advanced_content.form_contractor.page_type')]) + ; } } diff --git a/Resources/config/services.yml b/Resources/config/services.yml index ee1f587..e1c777d 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -6,6 +6,7 @@ services: - [setTranslationDomain, ['AdvancedContentBundle']] tags: - { name: sonata.admin, manager_type: orm, group: content.label, label: content.label, show_in_dashboard: true} + sherlockode_advanced_content.admin.page_type: class: Sherlockode\SonataAdvancedContentBundle\Admin\PageTypeAdmin arguments: [~, ~, ~] @@ -13,6 +14,7 @@ services: - [setTranslationDomain, ['AdvancedContentBundle']] tags: - { name: sonata.admin, manager_type: orm, group: content.label, label: page_type.label, show_in_dashboard: true} + sherlockode_advanced_content.admin.page: class: Sherlockode\SonataAdvancedContentBundle\Admin\PageAdmin arguments: [~, ~, ~] @@ -23,7 +25,6 @@ services: tags: - { name: sonata.admin, manager_type: orm, group: content.label, label: page.label, show_in_dashboard: true} - sherlockode_advanced_content.admin.page_extesnion: class: Sherlockode\SonataAdvancedContentBundle\Admin\Extension\PageExtension calls: @@ -35,3 +36,27 @@ services: class: Sherlockode\SonataAdvancedContentBundle\EventListener\MenuBuilderListener tags: - { name: kernel.event_listener, event: sonata.admin.event.configure.menu.sidebar, method: addMenuItems } + + sherlockode_advanced_content.form_contractor.content: + class: Sherlockode\SonataAdvancedContentBundle\Admin\FormContractor + calls: + - [ setFormType, [ 'Sherlockode\AdvancedContentBundle\Form\Type\ContentType' ] ] + arguments: + - '@form.factory' + - '@form.registry' + + sherlockode_advanced_content.form_contractor.page_type: + class: Sherlockode\SonataAdvancedContentBundle\Admin\FormContractor + calls: + - [ setFormType, [ 'Sherlockode\AdvancedContentBundle\Form\Type\PageTypeType' ] ] + arguments: + - '@form.factory' + - '@form.registry' + + sherlockode_advanced_content.form_contractor.page: + class: Sherlockode\SonataAdvancedContentBundle\Admin\FormContractor + calls: + - [ setFormType, [ 'Sherlockode\AdvancedContentBundle\Form\Type\PageType' ] ] + arguments: + - '@form.factory' + - '@form.registry' diff --git a/SherlockodeSonataAdvancedContentBundle.php b/SherlockodeSonataAdvancedContentBundle.php index 3267ec2..d8c4b7e 100644 --- a/SherlockodeSonataAdvancedContentBundle.php +++ b/SherlockodeSonataAdvancedContentBundle.php @@ -3,6 +3,7 @@ namespace Sherlockode\SonataAdvancedContentBundle; use Sherlockode\SonataAdvancedContentBundle\DependencyInjection\Compiler\AdminPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -10,6 +11,6 @@ class SherlockodeSonataAdvancedContentBundle extends Bundle { public function build(ContainerBuilder $container) { - $container->addCompilerPass(new AdminPass()); + $container->addCompilerPass(new AdminPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); } }