Passed
Branch master (046a17)
by Mathieu
02:22
created

addMapperProperty()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 14
rs 9.6111
c 0
b 0
f 0
cc 5
nc 5
nop 2
1
<?php
2
3
namespace Neimheadh\SonataAnnotationBundle\Reader;
4
5
use Doctrine\Common\Annotations\Reader;
6
use InvalidArgumentException;
7
use Neimheadh\SonataAnnotationBundle\Annotation\ActionAnnotationInterface;
8
use Neimheadh\SonataAnnotationBundle\Annotation\PositionAnnotationInterface;
9
use ReflectionClass;
10
use Sonata\AdminBundle\Datagrid\DatagridMapper;
11
use Sonata\AdminBundle\Datagrid\ListMapper;
12
use Sonata\AdminBundle\Form\FormMapper;
13
use Sonata\AdminBundle\Mapper\MapperInterface;
14
use Sonata\AdminBundle\Show\ShowMapper;
15
16
/**
17
 * Field configuration reader.
18
 */
19
class AbstractFieldConfigurationReader extends AbstractReader
20
{
21
22
    /**
23
     * Associated annotation class.
24
     *
25
     * @var string|null
26
     */
27
    protected ?string $annotationClass;
28
29
    /**
30
     * @param Reader  $annotationReader Doctrine annotation reader.
31
     * @param ?string $annotationClass  Associated annotation class.
32
     */
33
    public function __construct(
34
        Reader $annotationReader,
35
        ?string $annotationClass = null
36
    ) {
37
        parent::__construct($annotationReader);
38
        $this->annotationClass = $annotationClass;
39
    }
40
41
    /**
42
     * Build list field configurations.
43
     *
44
     * @param ReflectionClass $class  Entity class.
45
     * @param MapperInterface $mapper Admin mapper.
46
     *
47
     * @return void
48
     */
49
    public function configureFields(
50
        ReflectionClass $class,
51
        MapperInterface $mapper
52
    ): void {
53
        $this->configureReaderFields(
54
            $class,
55
            $mapper,
56
            $this->annotationClass
57
        );
58
    }
59
60
    /**
61
     * Configure fields with positions.
62
     *
63
     * @param ReflectionClass $class           Read entity class.
64
     * @param MapperInterface $mapper          Admin mapper.
65
     * @param string|null     $annotationClass Filtered annotation class.
66
     * @param string|null     $action          Current action.
67
     *
68
     * @return void
69
     */
70
    protected function configureReaderFields(
71
        ReflectionClass $class,
72
        MapperInterface $mapper,
73
        ?string $annotationClass = null,
74
        ?string $action = null
75
    ): void {
76
        $fields = $this->loadPositionAnnotationFields(
77
            $class,
78
            $annotationClass,
79
            $action
80
        );
81
        array_walk(
82
            $fields,
83
            fn(array $field) => $this->addMapperProperty($mapper, $field)
84
        );
85
    }
86
87
    /**
88
     * Check if given annotation is supported.
89
     *
90
     * @param object      $annotation Annotation.
91
     * @param string|null $action     Current action.
92
     *
93
     * @return bool
94
     */
95
    protected function isSupported(object $annotation, ?string $action): bool
96
    {
97
        return !$annotation instanceof ActionAnnotationInterface
98
            || $action === null
99
            || !isset($annotation->action)
100
            || $annotation->action === $action;
101
    }
102
103
    /**
104
     * Add property to datagrid mapper.
105
     *
106
     * @param DatagridMapper $mapper   Datagrid mapper.
107
     * @param string         $name     Property name.
108
     * @param array          $settings Annotation settings.
109
     *
110
     * @return void
111
     */
112
    private function addDatagridProperty(
113
        DatagridMapper $mapper,
114
        string $name,
115
        array $settings
116
    ): void {
117
        $mapper->add(
118
            $name,
119
            $settings[0] ?? null,
120
            $settings[1] ?? [],
121
            $settings[2] ?? [],
122
        );
123
    }
124
125
    /**
126
     * Add property to form mapper.
127
     *
128
     * @param FormMapper $mapper   Datagrid mapper.
129
     * @param string     $name     Property name.
130
     * @param array      $settings Annotation settings.
131
     *
132
     * @return void
133
     */
134
    private function addFormProperty(
135
        FormMapper $mapper,
136
        string $name,
137
        array $settings
138
    ): void {
139
        $mapper->add(
140
            $name,
141
            $settings[0] ?? null,
142
            $settings[1] ?? [],
143
            $settings[2] ?? [],
144
        );
145
    }
146
147
    /**
148
     * Add property to list mapper.
149
     *
150
     * @param ListMapper $mapper   List mapper.
151
     * @param string     $name     Property name.
152
     * @param array      $settings Annotation settings.
153
     *
154
     * @return void
155
     */
156
    private function addListProperty(
157
        ListMapper $mapper,
158
        string $name,
159
        array $settings
160
    ): void {
161
        $mapper->add(
162
            $name,
163
            $settings[0] ?? null,
164
            $settings[1] ?? [],
165
        );
166
    }
167
168
    /**
169
     * Add a property to the given mapper.
170
     *
171
     * @param MapperInterface $mapper   Admin mapper.
172
     * @param array           $property Property information.
173
     *
174
     * @return void
175
     */
176
    private function addMapperProperty(
177
        MapperInterface $mapper,
178
        array $property
179
    ): void {
180
        $settings = $property['annotation']->getSettings();
181
182
        if ($mapper instanceof DatagridMapper) {
183
            $this->addDatagridProperty($mapper, $property['name'], $settings);
184
        } elseif ($mapper instanceof FormMapper) {
185
            $this->addFormProperty($mapper, $property['name'], $settings);
186
        } elseif ($mapper instanceof ListMapper) {
187
            $this->addListProperty($mapper, $property['name'], $settings);
188
        } elseif ($mapper instanceof ShowMapper) {
189
            $this->addShowProperty($mapper, $property['name'], $settings);
190
        }
191
    }
192
193
    /**
194
     * Add property to show mapper.
195
     *
196
     * @param ShowMapper $mapper   Show mapper.
197
     * @param string     $name     Property name.
198
     * @param array      $settings Annotation settings.
199
     *
200
     * @return void
201
     */
202
    private function addShowProperty(
203
        ShowMapper $mapper,
204
        string $name,
205
        array $settings
206
    ): void {
207
        $mapper->add(
208
            $name,
209
            $settings[0] ?? null,
210
            $settings[1] ?? [],
211
        );
212
    }
213
214
    /**
215
     * Load class properties with position annotation.
216
     *
217
     * @param ReflectionClass $class           Entity class.
218
     * @param string|null     $annotationClass Reader annotation class.
219
     * @param string|null     $action          Current action.
220
     *
221
     * @return array
222
     */
223
    private function loadPositionAnnotationFields(
224
        ReflectionClass $class,
225
        ?string $annotationClass,
226
        ?string $action
227
    ): array {
228
        $propertiesAndMethods = array_merge(
229
            $this->getClassPropertiesAnnotations($class, $annotationClass),
230
            $this->getClassMethodsAnnotations($class, $annotationClass)
231
        );
232
233
        $propertiesWithPosition = [];
234
        $propertiesWithoutPosition = [];
235
236
        foreach ($propertiesAndMethods as $name => $annotations) {
237
            /** @var PositionAnnotationInterface $annotation */
238
            foreach ($annotations as $annotation) {
239
                if (!$this->isSupported($annotation, $action)) {
240
                    continue;
241
                }
242
243
                $name = $this->getAnnotationFieldName($name, $annotation);
244
245
                $this->stackProperty(
246
                    $name,
247
                    $annotation,
248
                    $propertiesWithPosition,
249
                    $propertiesWithoutPosition
250
                );
251
            }
252
        }
253
254
        ksort($propertiesWithPosition);
255
256
        return array_merge(
257
            $propertiesWithPosition,
258
            $propertiesWithoutPosition
259
        );
260
    }
261
262
    /**
263
     * Stack given annotation as a property with or without position.
264
     *
265
     * @param string $name                      Property name.
266
     * @param object $annotation                Annotation.
267
     * @param array  $propertiesWithPosition    Properties with position stack.
268
     * @param array  $propertiesWithoutPosition Properties without position
269
     *                                          stack.
270
     *
271
     * @return void
272
     */
273
    protected function stackProperty(
274
        string $name,
275
        object $annotation,
276
        array &$propertiesWithPosition,
277
        array &$propertiesWithoutPosition
278
    ): void {
279
        if (!isset($annotation->position)) {
280
            $propertiesWithoutPosition[] = [
281
                'name' => $name,
282
                'annotation' => $annotation,
283
            ];
284
285
            return;
286
        }
287
288
        if (array_key_exists(
289
            $annotation->position,
290
            $propertiesWithPosition
291
        )) {
292
            throw new InvalidArgumentException(
293
                sprintf(
294
                    'Position "%s" is already in use by "%s", try setting a different position for "%s".',
295
                    $annotation->position,
296
                    $propertiesWithPosition[$annotation->position]['name'],
297
                    $name
298
                )
299
            );
300
        }
301
302
        $propertiesWithPosition[$annotation->position] = [
303
            'name' => $name,
304
            'annotation' => $annotation,
305
        ];
306
    }
307
308
}