Passed
Push — develop ( 5f199e...56f9a8 )
by Mathieu
02:20
created

loadDefaultFields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 14
rs 10
c 0
b 0
f 0
cc 2
nc 2
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 ReflectionProperty;
11
use Sonata\AdminBundle\Datagrid\DatagridMapper;
12
use Sonata\AdminBundle\Datagrid\ListMapper;
13
use Sonata\AdminBundle\Form\FormMapper;
14
use Sonata\AdminBundle\Mapper\MapperInterface;
15
use Sonata\AdminBundle\Show\ShowMapper;
16
use Symfony\Component\PropertyAccess\PropertyAccess;
17
18
/**
19
 * Field configuration reader.
20
 */
21
class AbstractFieldConfigurationReader extends AbstractReader
22
{
23
24
    /**
25
     * Associated annotation class.
26
     *
27
     * @var string|null
28
     */
29
    protected ?string $annotationClass;
30
31
    /**
32
     * @param Reader  $annotationReader Doctrine annotation reader.
33
     * @param ?string $annotationClass  Associated annotation class.
34
     */
35
    public function __construct(
36
        Reader $annotationReader,
37
        ?string $annotationClass = null
38
    ) {
39
        parent::__construct($annotationReader);
40
        $this->annotationClass = $annotationClass;
41
    }
42
43
    /**
44
     * Build list field configurations.
45
     *
46
     * @param ReflectionClass $class  Entity class.
47
     * @param MapperInterface $mapper Admin mapper.
48
     *
49
     * @return void
50
     */
51
    public function configureFields(
52
        ReflectionClass $class,
53
        MapperInterface $mapper
54
    ): void {
55
        $this->configureReaderFields(
56
            $class,
57
            $mapper,
58
            $this->annotationClass
59
        );
60
    }
61
62
    /**
63
     * Configure fields with positions.
64
     *
65
     * @param ReflectionClass $class           Read entity class.
66
     * @param MapperInterface $mapper          Admin mapper.
67
     * @param string|null     $annotationClass Filtered annotation class.
68
     * @param string|null     $action          Current action.
69
     *
70
     * @return void
71
     */
72
    protected function configureReaderFields(
73
        ReflectionClass $class,
74
        MapperInterface $mapper,
75
        ?string $annotationClass = null,
76
        ?string $action = null
77
    ): void {
78
        $fields = $this->loadPositionAnnotationFields(
79
            $class,
80
            $annotationClass,
81
            $action
82
        );
83
84
        if (empty($fields)) {
85
            $fields = $this->loadDefaultFields($class, $annotationClass);
0 ignored issues
show
Bug introduced by
It seems like $annotationClass can also be of type null; however, parameter $annotationClass of Neimheadh\SonataAnnotati...er::loadDefaultFields() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

85
            $fields = $this->loadDefaultFields($class, /** @scrutinizer ignore-type */ $annotationClass);
Loading history...
86
        }
87
88
        array_walk(
89
            $fields,
90
            fn(array $field) => $this->addMapperProperty($mapper, $field)
91
        );
92
    }
93
94
    /**
95
     * Check if given annotation is supported.
96
     *
97
     * @param object      $annotation Annotation.
98
     * @param string|null $action     Current action.
99
     *
100
     * @return bool
101
     */
102
    protected function isSupported(object $annotation, ?string $action): bool
103
    {
104
        return !$annotation instanceof ActionAnnotationInterface
105
            || $action === null
106
            || !isset($annotation->action)
107
            || $annotation->action === $action;
108
    }
109
110
    /**
111
     * Load default fields.
112
     *
113
     * @param ReflectionClass $class  Entity class.
114
     * @param string|null     $annotationClass Reader annotation class.
115
     *
116
     * @return array
117
     */
118
    protected function loadDefaultFields(
119
        ReflectionClass $class,
120
        string $annotationClass
121
    ): array {
122
        $properties = [];
123
124
        foreach ($class->getProperties() as $property) {
125
            $properties[] = [
126
                'name' => $property->getName(),
127
                'annotation' => new $annotationClass(),
128
            ];
129
        }
130
131
        return $properties;
132
    }
133
134
    /**
135
     * Load class properties with position annotation.
136
     *
137
     * @param ReflectionClass $class           Entity class.
138
     * @param string|null     $annotationClass Reader annotation class.
139
     * @param string|null     $action          Current action.
140
     *
141
     * @return array
142
     */
143
    protected function loadPositionAnnotationFields(
144
        ReflectionClass $class,
145
        ?string $annotationClass,
146
        ?string $action
147
    ): array {
148
        $propertiesAndMethods = array_merge(
149
            $this->getClassPropertiesAnnotations($class, $annotationClass),
150
            $this->getClassMethodsAnnotations($class, $annotationClass)
151
        );
152
153
        $propertiesWithPosition = [];
154
        $propertiesWithoutPosition = [];
155
156
        foreach ($propertiesAndMethods as $name => $annotations) {
157
            /** @var PositionAnnotationInterface $annotation */
158
            foreach ($annotations as $annotation) {
159
                if (!$this->isSupported($annotation, $action)) {
160
                    continue;
161
                }
162
163
                $name = $this->getAnnotationFieldName($name, $annotation);
164
165
                $this->stackProperty(
166
                    $name,
167
                    $annotation,
168
                    $propertiesWithPosition,
169
                    $propertiesWithoutPosition
170
                );
171
            }
172
        }
173
174
        ksort($propertiesWithPosition);
175
176
        return array_merge(
177
            $propertiesWithPosition,
178
            $propertiesWithoutPosition
179
        );
180
    }
181
182
    /**
183
     * Stack given annotation as a property with or without position.
184
     *
185
     * @param string $name                      Property name.
186
     * @param object $annotation                Annotation.
187
     * @param array  $propertiesWithPosition    Properties with position stack.
188
     * @param array  $propertiesWithoutPosition Properties without position
189
     *                                          stack.
190
     *
191
     * @return void
192
     */
193
    protected function stackProperty(
194
        string $name,
195
        object $annotation,
196
        array &$propertiesWithPosition,
197
        array &$propertiesWithoutPosition
198
    ): void {
199
        if (!isset($annotation->position)) {
200
            $propertiesWithoutPosition[] = [
201
                'name' => $name,
202
                'annotation' => $annotation,
203
            ];
204
205
            return;
206
        }
207
208
        if (array_key_exists(
209
            $annotation->position,
210
            $propertiesWithPosition
211
        )) {
212
            throw new InvalidArgumentException(
213
                sprintf(
214
                    'Position "%s" is already in use by "%s", try setting a different position for "%s".',
215
                    $annotation->position,
216
                    $propertiesWithPosition[$annotation->position]['name'],
217
                    $name
218
                )
219
            );
220
        }
221
222
        $propertiesWithPosition[$annotation->position] = [
223
            'name' => $name,
224
            'annotation' => $annotation,
225
        ];
226
    }
227
228
    /**
229
     * Add property to datagrid mapper.
230
     *
231
     * @param DatagridMapper $mapper   Datagrid mapper.
232
     * @param string         $name     Property name.
233
     * @param array          $settings Annotation settings.
234
     *
235
     * @return void
236
     */
237
    private function addDatagridProperty(
238
        DatagridMapper $mapper,
239
        string $name,
240
        array $settings
241
    ): void {
242
        $mapper->add(
243
            $name,
244
            $settings[0] ?? null,
245
            $settings[1] ?? [],
246
            $settings[2] ?? [],
247
        );
248
    }
249
250
    /**
251
     * Add property to form mapper.
252
     *
253
     * @param FormMapper $mapper   Datagrid mapper.
254
     * @param string     $name     Property name.
255
     * @param array      $settings Annotation settings.
256
     *
257
     * @return void
258
     */
259
    private function addFormProperty(
260
        FormMapper $mapper,
261
        string $name,
262
        array $settings
263
    ): void {
264
        $mapper->add(
265
            $name,
266
            $settings[0] ?? null,
267
            $settings[1] ?? [],
268
            $settings[2] ?? [],
269
        );
270
    }
271
272
    /**
273
     * Add property to list mapper.
274
     *
275
     * @param ListMapper $mapper   List mapper.
276
     * @param string     $name     Property name.
277
     * @param array      $settings Annotation settings.
278
     *
279
     * @return void
280
     */
281
    private function addListProperty(
282
        ListMapper $mapper,
283
        string $name,
284
        array $settings
285
    ): void {
286
        $mapper->add(
287
            $name,
288
            $settings[0] ?? null,
289
            $settings[1] ?? [],
290
        );
291
    }
292
293
    /**
294
     * Add a property to the given mapper.
295
     *
296
     * @param MapperInterface $mapper   Admin mapper.
297
     * @param array           $property Property information.
298
     *
299
     * @return void
300
     */
301
    private function addMapperProperty(
302
        MapperInterface $mapper,
303
        array $property
304
    ): void {
305
        $settings = $property['annotation']->getSettings();
306
307
        if ($mapper instanceof DatagridMapper) {
308
            $this->addDatagridProperty($mapper, $property['name'], $settings);
309
        } elseif ($mapper instanceof FormMapper) {
310
            $this->addFormProperty($mapper, $property['name'], $settings);
311
        } elseif ($mapper instanceof ListMapper) {
312
            $this->addListProperty($mapper, $property['name'], $settings);
313
        } elseif ($mapper instanceof ShowMapper) {
314
            $this->addShowProperty($mapper, $property['name'], $settings);
315
        }
316
    }
317
318
    /**
319
     * Add property to show mapper.
320
     *
321
     * @param ShowMapper $mapper   Show mapper.
322
     * @param string     $name     Property name.
323
     * @param array      $settings Annotation settings.
324
     *
325
     * @return void
326
     */
327
    private function addShowProperty(
328
        ShowMapper $mapper,
329
        string $name,
330
        array $settings
331
    ): void {
332
        $mapper->add(
333
            $name,
334
            $settings[0] ?? null,
335
            $settings[1] ?? [],
336
        );
337
    }
338
}