Completed
Push — master ( f335a7...6a7f39 )
by Grégoire
12s
created

FormContractor   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 3
dl 0
loc 192
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C fixFieldDescription() 0 31 7
A getFormFactory() 0 4 1
A getFormBuilder() 0 4 1
D getDefaultOptions() 0 85 14
A hasAssociation() 0 9 1
A checkFormClass() 0 6 1
1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\DoctrineORMAdminBundle\Builder;
13
14
use Doctrine\ORM\Mapping\ClassMetadataInfo;
15
use Sonata\AdminBundle\Admin\AdminInterface;
16
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
17
use Sonata\AdminBundle\Builder\FormContractorInterface;
18
use Sonata\AdminBundle\Form\Type\AdminType;
19
use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
20
use Sonata\AdminBundle\Form\Type\ModelHiddenType;
21
use Sonata\AdminBundle\Form\Type\ModelListType;
22
use Sonata\AdminBundle\Form\Type\ModelType;
23
use Sonata\AdminBundle\Form\Type\ModelTypeList;
24
use Sonata\CoreBundle\Form\Type\CollectionType;
25
use Symfony\Component\Form\Extension\Core\Type\FormType;
26
use Symfony\Component\Form\FormFactoryInterface;
27
28
class FormContractor implements FormContractorInterface
29
{
30
    /**
31
     * NEXT_MAJOR: remove this property.
32
     *
33
     * @deprecated since version 3.0.4, to be removed in 4.0
34
     *
35
     * @var FormFactoryInterface
36
     */
37
    protected $fieldFactory;
38
39
    /**
40
     * @var FormFactoryInterface
41
     */
42
    protected $formFactory;
43
44
    /**
45
     * @param FormFactoryInterface $formFactory
46
     */
47
    public function __construct(FormFactoryInterface $formFactory)
48
    {
49
        $this->formFactory = $formFactory;
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function fixFieldDescription(AdminInterface $admin, FieldDescriptionInterface $fieldDescription)
56
    {
57
        if ($admin->getModelManager()->hasMetadata($admin->getClass())) {
58
            $metadata = $admin->getModelManager()->getMetadata($admin->getClass());
59
60
            // set the default field mapping
61
            if (isset($metadata->fieldMappings[$fieldDescription->getName()])) {
62
                $fieldDescription->setFieldMapping($metadata->fieldMappings[$fieldDescription->getName()]);
63
            }
64
65
            // set the default association mapping
66
            if (isset($metadata->associationMappings[$fieldDescription->getName()])) {
67
                $fieldDescription->setAssociationMapping($metadata->associationMappings[$fieldDescription->getName()]);
68
            }
69
        }
70
71
        if (!$fieldDescription->getType()) {
72
            throw new \RuntimeException(sprintf(
73
                'Please define a type for field `%s` in `%s`',
74
                $fieldDescription->getName(),
75
                get_class($admin)
76
            ));
77
        }
78
79
        $fieldDescription->setAdmin($admin);
80
        $fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'standard'));
81
82
        if ($this->hasAssociation($fieldDescription) || $fieldDescription->getOption('admin_code')) {
83
            $admin->attachAdminClass($fieldDescription);
84
        }
85
    }
86
87
    /**
88
     * @return FormFactoryInterface
89
     */
90
    public function getFormFactory()
91
    {
92
        return $this->formFactory;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function getFormBuilder($name, array $options = [])
99
    {
100
        return $this->getFormFactory()->createNamedBuilder($name, FormType::class, null, $options);
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function getDefaultOptions($type, FieldDescriptionInterface $fieldDescription)
107
    {
108
        $options = [];
109
        $options['sonata_field_description'] = $fieldDescription;
110
111
        // NEXT_MAJOR: Check only against FQCNs when dropping support for form mapping
112
        if (in_array($type, [
113
            'sonata_type_model',
114
            'sonata_type_model_list',
115
            'sonata_type_model_hidden',
116
            'sonata_type_model_autocomplete',
117
        ], true) || $this->checkFormClass($type, [
118
            ModelType::class,
119
            ModelTypeList::class,
120
            ModelListType::class,
121
            ModelHiddenType::class,
122
            ModelAutocompleteType::class,
123
        ])) {
124
            if ('list' === $fieldDescription->getOption('edit')) {
125
                throw new \LogicException(
126
                    'The `sonata_type_model` type does not accept an `edit` option anymore,'
127
                    .' please review the UPGRADE-2.1.md file from the SonataAdminBundle'
128
                );
129
            }
130
131
            $options['class'] = $fieldDescription->getTargetEntity();
132
            $options['model_manager'] = $fieldDescription->getAdmin()->getModelManager();
133
134
            // NEXT_MAJOR: Check only against FQCNs when dropping support for form mapping
135
            if ('sonata_type_model_autocomplete' === $type || $this->checkFormClass($type, [ModelAutocompleteType::class])) {
136
                if (!$fieldDescription->getAssociationAdmin()) {
137
                    throw new \RuntimeException(sprintf(
138
                        'The current field `%s` is not linked to an admin.'
139
                        .' Please create one for the target entity: `%s`',
140
                        $fieldDescription->getName(),
141
                        $fieldDescription->getTargetEntity()
142
                    ));
143
                }
144
            }
145
            // NEXT_MAJOR: Check only against FQCNs when dropping support for form mapping
146
        } elseif ('sonata_type_admin' === $type || $this->checkFormClass($type, [AdminType::class])) {
147
            if (!$fieldDescription->getAssociationAdmin()) {
148
                throw new \RuntimeException(sprintf(
149
                    'The current field `%s` is not linked to an admin.'
150
                    .' Please create one for the target entity : `%s`',
151
                    $fieldDescription->getName(),
152
                    $fieldDescription->getTargetEntity()
153
                ));
154
            }
155
156
            if (!in_array($fieldDescription->getMappingType(), [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
157
                throw new \RuntimeException(sprintf(
158
                    'You are trying to add `sonata_type_admin` field `%s` which is not One-To-One or  Many-To-One.'
159
                    .' Maybe you want `sonata_type_collection` instead?',
160
                    $fieldDescription->getName()
161
                ));
162
            }
163
164
            // set sensitive default value to have a component working fine out of the box
165
            $options['btn_add'] = false;
166
            $options['delete'] = false;
167
168
            $options['data_class'] = $fieldDescription->getAssociationAdmin()->getClass();
169
            $fieldDescription->setOption('edit', $fieldDescription->getOption('edit', 'admin'));
170
            // NEXT_MAJOR: Check only against FQCNs when dropping support for form mapping
171
        } elseif ('sonata_type_collection' === $type || $this->checkFormClass($type, [CollectionType::class])) {
172
            if (!$fieldDescription->getAssociationAdmin()) {
173
                throw new \RuntimeException(sprintf(
174
                    'The current field `%s` is not linked to an admin.'
175
                    .' Please create one for the target entity : `%s`',
176
                    $fieldDescription->getName(),
177
                    $fieldDescription->getTargetEntity()
178
                ));
179
            }
180
181
            $options['type'] = AdminType::class;
182
            $options['modifiable'] = true;
183
            $options['type_options'] = [
184
                'sonata_field_description' => $fieldDescription,
185
                'data_class' => $fieldDescription->getAssociationAdmin()->getClass(),
186
            ];
187
        }
188
189
        return $options;
190
    }
191
192
    /**
193
     * @param FieldDescriptionInterface $fieldDescription
194
     *
195
     * @return bool
196
     */
197
    private function hasAssociation(FieldDescriptionInterface $fieldDescription)
198
    {
199
        return in_array($fieldDescription->getMappingType(), [
200
            ClassMetadataInfo::ONE_TO_MANY,
201
            ClassMetadataInfo::MANY_TO_MANY,
202
            ClassMetadataInfo::MANY_TO_ONE,
203
            ClassMetadataInfo::ONE_TO_ONE,
204
        ]);
205
    }
206
207
    /**
208
     * @param string $type
209
     * @param array  $classes
210
     *
211
     * @return array
212
     */
213
    private function checkFormClass($type, $classes)
214
    {
215
        return array_filter($classes, function ($subclass) use ($type) {
216
            return is_a($type, $subclass, true);
217
        });
218
    }
219
}
220