Skip to content

Commit 79824c6

Browse files
committed
Sonata 4 compatibility
1 parent fe8bd8a commit 79824c6

File tree

8 files changed

+299
-89
lines changed

8 files changed

+299
-89
lines changed

Admin/ContentAdmin.php

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,23 @@
66
use Sherlockode\AdvancedContentBundle\Model\ContentInterface;
77
use Sonata\AdminBundle\Admin\AbstractAdmin;
88
use Sonata\AdminBundle\Datagrid\ListMapper;
9+
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
910
use Sonata\AdminBundle\Form\FormMapper;
1011
use Symfony\Component\Form\FormBuilderInterface;
1112

1213
class ContentAdmin extends AbstractAdmin
1314
{
1415
protected $baseRouteName = 'admin_afb_content';
1516

16-
public function getFormTheme()
17+
protected function configure(): void
1718
{
18-
return array_merge(
19+
$this->setFormTheme(array_merge(
1920
parent::getFormTheme(),
2021
['@SherlockodeAdvancedContent/Form/content.html.twig']
21-
);
22+
));
2223
}
2324

24-
public function configureFormFields(FormMapper $form)
25+
public function configureFormFields(FormMapper $form): void
2526
{
2627
$form->tab('content.form.tabs.label')
2728
->with('content.form.tabs.general', [
@@ -42,31 +43,7 @@ public function configureFormFields(FormMapper $form)
4243
->end(); // init the groups data for this admin class
4344
}
4445

45-
/**
46-
* Create ContentType form
47-
*
48-
* @return FormBuilderInterface
49-
*
50-
* @throws \Exception
51-
*/
52-
private function getCustomFormBuilder()
53-
{
54-
return $this->getFormContractor()
55-
->getFormFactory()
56-
->createNamedBuilder($this->getUniqid(), ContentType::class, null, $this->formOptions);
57-
}
58-
59-
public function getFormBuilder()
60-
{
61-
$this->formOptions['data_class'] = $this->getClass();
62-
63-
$formBuilder = $this->getCustomFormBuilder();
64-
$this->defineFormBuilder($formBuilder);
65-
66-
return $formBuilder;
67-
}
68-
69-
public function configureListFields(ListMapper $list)
46+
public function configureListFields(ListMapper $list): void
7047
{
7148
$list
7249
->add('id', null, ['label' => 'content.id'])
@@ -80,9 +57,14 @@ public function configureListFields(ListMapper $list)
8057
;
8158
}
8259

83-
public function createQuery($context = 'list')
60+
/**
61+
* @param ProxyQueryInterface $query
62+
*
63+
* @return ProxyQueryInterface
64+
*/
65+
public function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
8466
{
85-
$query = parent::createQuery();
67+
$query = parent::configureQuery($query);
8668
$query->getQueryBuilder()
8769
->where('o.page IS NULL');
8870

@@ -94,7 +76,7 @@ public function createQuery($context = 'list')
9476
*
9577
* @return string
9678
*/
97-
public function toString($object)
79+
public function toString($object): string
9880
{
9981
if ($object instanceof ContentInterface && $object->getName()) {
10082
return $object->getName();

Admin/Extension/PageExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class PageExtension extends AbstractAdminExtension
1515
private $configurationManager;
1616

1717

18-
public function configureFormFields(FormMapper $formMapper)
18+
public function configureFormFields(FormMapper $formMapper): void
1919
{
2020
if ($formMapper->has('pageType')) {
2121
$formMapper->tab('page.form.tabs.label')

Admin/FormContractor.php

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
<?php
2+
3+
namespace Sherlockode\SonataAdvancedContentBundle\Admin;
4+
5+
use Sonata\AdminBundle\Builder\FormContractorInterface;
6+
use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
7+
use Sonata\AdminBundle\Form\Type\AdminType;
8+
use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
9+
use Sonata\AdminBundle\Form\Type\ModelHiddenType;
10+
use Sonata\AdminBundle\Form\Type\ModelListType;
11+
use Sonata\AdminBundle\Form\Type\ModelReferenceType;
12+
use Sonata\AdminBundle\Form\Type\ModelType;
13+
use Sonata\Form\Type\CollectionType;
14+
use Symfony\Component\Form\Extension\Core\Type\FormType;
15+
use Symfony\Component\Form\FormBuilderInterface;
16+
use Symfony\Component\Form\FormFactoryInterface;
17+
use Symfony\Component\Form\FormRegistryInterface;
18+
19+
class FormContractor implements FormContractorInterface
20+
{
21+
/**
22+
* @var FormFactoryInterface
23+
*/
24+
protected $formFactory;
25+
26+
/**
27+
* @var FormRegistryInterface
28+
*/
29+
protected $formRegistry;
30+
31+
/**
32+
* @var string
33+
*/
34+
protected $formType;
35+
36+
/**
37+
* @param FormFactoryInterface $formFactory
38+
* @param FormRegistryInterface $formRegistry
39+
*/
40+
public function __construct(FormFactoryInterface $formFactory, FormRegistryInterface $formRegistry)
41+
{
42+
$this->formFactory = $formFactory;
43+
$this->formRegistry = $formRegistry;
44+
}
45+
46+
/**
47+
* @param FieldDescriptionInterface $fieldDescription
48+
*
49+
* @return void
50+
*/
51+
public function fixFieldDescription(FieldDescriptionInterface $fieldDescription): void
52+
{
53+
$fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'standard'));
54+
55+
if ($fieldDescription->describesAssociation() || null !== $fieldDescription->getOption('admin_code')) {
56+
$fieldDescription->getAdmin()->attachAdminClass($fieldDescription);
57+
}
58+
}
59+
60+
/**
61+
* @param string $name
62+
* @param array $formOptions
63+
*
64+
* @return FormBuilderInterface
65+
*/
66+
public function getFormBuilder(string $name, array $formOptions = []): FormBuilderInterface
67+
{
68+
return $this->formFactory->createNamedBuilder($name, $this->formType, null, $formOptions);
69+
}
70+
71+
/**
72+
* @param string $formType
73+
*
74+
* @return $this
75+
*/
76+
public function setFormType(string $formType): self
77+
{
78+
$this->formType = $formType;
79+
80+
return $this;
81+
}
82+
83+
/**
84+
* @param string|null $type
85+
* @param FieldDescriptionInterface $fieldDescription
86+
* @param array $formOptions
87+
*
88+
* @return array|mixed[]
89+
*/
90+
final public function getDefaultOptions(
91+
?string $type,
92+
FieldDescriptionInterface $fieldDescription,
93+
array $formOptions = []
94+
): array {
95+
$options = [];
96+
$options['sonata_field_description'] = $fieldDescription;
97+
98+
if ($this->isAnyInstanceOf($type, [
99+
ModelType::class,
100+
ModelListType::class,
101+
ModelHiddenType::class,
102+
ModelAutocompleteType::class,
103+
ModelReferenceType::class,
104+
])) {
105+
$options['class'] = $fieldDescription->getTargetModel();
106+
$options['model_manager'] = $fieldDescription->getAdmin()->getModelManager();
107+
108+
if ($this->isAnyInstanceOf($type, [ModelAutocompleteType::class])) {
109+
if (!$fieldDescription->hasAssociationAdmin()) {
110+
throw new \InvalidArgumentException(sprintf(
111+
'The current field `%s` is not linked to an admin.'
112+
.' Please create one for the target model: `%s`.',
113+
$fieldDescription->getName(),
114+
$fieldDescription->getTargetModel() ?? ''
115+
));
116+
}
117+
}
118+
} elseif ($this->isAnyInstanceOf($type, [AdminType::class])) {
119+
if (!$fieldDescription->hasAssociationAdmin()) {
120+
throw new \InvalidArgumentException(sprintf(
121+
'The current field `%s` is not linked to an admin.'
122+
.' Please create one for the target model: `%s`.',
123+
$fieldDescription->getName(),
124+
$fieldDescription->getTargetModel() ?? ''
125+
));
126+
}
127+
128+
if (!$fieldDescription->describesSingleValuedAssociation()) {
129+
throw new \InvalidArgumentException(sprintf(
130+
'You are trying to add `%s` field `%s` which is not a One-To-One or Many-To-One association.'
131+
.' You SHOULD use `%s` instead.',
132+
AdminType::class,
133+
$fieldDescription->getName(),
134+
CollectionType::class
135+
));
136+
}
137+
138+
// set sensitive default value to have a component working fine out of the box
139+
$options['btn_add'] = false;
140+
$options['delete'] = false;
141+
142+
$options['data_class'] = $fieldDescription->getAssociationAdmin()->getClass();
143+
$options['empty_data'] = static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance();
144+
$fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'admin'));
145+
} elseif ($this->isAnyInstanceOf($type, [
146+
CollectionType::class,
147+
])) {
148+
if (!$fieldDescription->hasAssociationAdmin()) {
149+
throw new \InvalidArgumentException(sprintf(
150+
'The current field `%s` is not linked to an admin.'
151+
.' Please create one for the target model: `%s`.',
152+
$fieldDescription->getName(),
153+
$fieldDescription->getTargetModel() ?? ''
154+
));
155+
}
156+
157+
$options['type'] = AdminType::class;
158+
$options['modifiable'] = true;
159+
$options['type_options'] = $this->getDefaultAdminTypeOptions($fieldDescription, $formOptions);
160+
}
161+
162+
return $options;
163+
}
164+
165+
/**
166+
* @param string|null $type
167+
* @param array $classes
168+
*
169+
* @return bool
170+
*/
171+
private function isAnyInstanceOf(?string $type, array $classes): bool
172+
{
173+
if (null === $type) {
174+
return false;
175+
}
176+
177+
foreach ($classes as $class) {
178+
if (is_a($type, $class, true)) {
179+
return true;
180+
}
181+
}
182+
183+
// handle form type inheritance and check all parent types
184+
$resolvedType = $this->formRegistry->getType($type);
185+
$parentType = $resolvedType->getParent();
186+
if (null !== $parentType) {
187+
$parentType = \get_class($parentType->getInnerType());
188+
189+
// all types have "Symfony\Component\Form\Extension\Core\Type\FormType" as parent
190+
// so we ignore it here for performance reasons
191+
if (FormType::class !== $parentType) {
192+
return $this->isAnyInstanceOf($parentType, $classes);
193+
}
194+
}
195+
196+
return false;
197+
}
198+
199+
/**
200+
* @param FieldDescriptionInterface $fieldDescription
201+
* @param array $formOptions
202+
*
203+
* @return array
204+
*/
205+
private function getDefaultAdminTypeOptions(FieldDescriptionInterface $fieldDescription, array $formOptions): array
206+
{
207+
$typeOptions = [
208+
'sonata_field_description' => $fieldDescription,
209+
'data_class' => $fieldDescription->getAssociationAdmin()->getClass(),
210+
'empty_data' => static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance(),
211+
];
212+
213+
if (isset($formOptions['by_reference'])) {
214+
$typeOptions['collection_by_reference'] = $formOptions['by_reference'];
215+
}
216+
217+
return $typeOptions;
218+
}
219+
}

Admin/PageAdmin.php

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ class PageAdmin extends AbstractAdmin
1717
*/
1818
private $localeProvider;
1919

20-
public function getFormTheme()
20+
protected function configure(): void
2121
{
22-
return array_merge(
22+
$this->setFormTheme(array_merge(
2323
parent::getFormTheme(),
2424
['@SherlockodeAdvancedContent/Form/content.html.twig']
25-
);
25+
));
2626
}
2727

28-
public function configureFormFields(FormMapper $form)
28+
public function configureFormFields(FormMapper $form): void
2929
{
3030
$form->tab('page.form.tabs.label')
3131
->with('page.form.tabs.general')->end(); // init the groups data for this admin class
@@ -69,37 +69,14 @@ public function configureFormFields(FormMapper $form)
6969
$form->end();
7070
}
7171

72-
public function getFormBuilder()
73-
{
74-
$this->formOptions['data_class'] = $this->getClass();
75-
76-
$formBuilder = $this->getCustomFormBuilder();
77-
$this->defineFormBuilder($formBuilder);
78-
79-
return $formBuilder;
80-
}
81-
82-
/**
83-
* Create PageType form
84-
*
85-
* @return FormBuilderInterface
86-
*
87-
* @throws \Exception
88-
*/
89-
private function getCustomFormBuilder()
90-
{
91-
return $this->getFormContractor()
92-
->getFormFactory()
93-
->createNamedBuilder($this->getUniqid(), PageType::class, null, $this->formOptions);
94-
}
95-
96-
public function configureListFields(ListMapper $list)
72+
public function configureListFields(ListMapper $list): void
9773
{
9874
$list
9975
->add('pageIdentifier', null, ['label' => 'page.form.page_identifier'])
10076
->add('title', null, [
10177
'label' => 'page.form.title',
102-
'template' => '@SherlockodeSonataAdvancedContent/Page/title.html.twig'
78+
'template' => '@SherlockodeSonataAdvancedContent/Page/title.html.twig',
79+
'virtual_field' => true,
10380
])
10481
->add('status', null, [
10582
'label' => 'page.form.status',
@@ -119,7 +96,7 @@ public function configureListFields(ListMapper $list)
11996
*
12097
* @return string
12198
*/
122-
public function toString($object)
99+
public function toString($object): string
123100
{
124101
if ($object instanceof PageInterface && $object->getPageIdentifier()) {
125102
return $object->getPageIdentifier();

0 commit comments

Comments
 (0)