Passed
Push — develop ( 58e758...ce87ff )
by Mathieu
02:29
created

AbstractFieldConfigurationReader::isSupported()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 4
nc 4
nop 2
1
<?php
2
3
namespace KunicMarko\SonataAnnotationBundle\Reader;
4
5
use Doctrine\Common\Annotations\Reader;
6
use InvalidArgumentException;
7
use KunicMarko\SonataAnnotationBundle\Annotation\ActionAnnotationInterface;
8
use KunicMarko\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 a property to the given mapper.
105
     *
106
     * @param MapperInterface $mapper   Admin mapper.
107
     * @param array           $property Property information.
108
     *
109
     * @return void
110
     */
111
    private function addMapperProperty(
112
        MapperInterface $mapper,
113
        array $property
114
    ): void {
115
        $settings = $property['annotation']->getSettings();
116
117
        if ($mapper instanceof DatagridMapper
118
            || $mapper instanceof FormMapper
119
        ) {
120
            $mapper->add(
121
                $property['name'],
122
                array_shift($settings),
123
                array_shift($settings) ?: [],
124
                array_shift($settings) ?: [],
125
            );
126
        } elseif ($mapper instanceof ListMapper
127
            || $mapper instanceof ShowMapper
128
        ) {
129
            $mapper->add(
130
                $property['name'],
131
                array_shift($settings),
132
                array_shift($settings) ?: [],
133
            );
134
        }
135
    }
136
137
    /**
138
     * Load class properties with position annotation.
139
     *
140
     * @param ReflectionClass $class           Entity class.
141
     * @param string|null     $annotationClass Reader annotation class.
142
     * @param string|null     $action          Current action.
143
     *
144
     * @return array
145
     */
146
    private function loadPositionAnnotationFields(
147
        ReflectionClass $class,
148
        ?string $annotationClass,
149
        ?string $action
150
    ): array {
151
        $propertiesAndMethods = array_merge(
152
            $this->getClassPropertiesAnnotations($class, $annotationClass),
153
            $this->getClassMethodsAnnotations($class, $annotationClass)
154
        );
155
156
        $propertiesWithPosition = [];
157
        $propertiesWithoutPosition = [];
158
159
        foreach ($propertiesAndMethods as $name => $annotations) {
160
            /** @var PositionAnnotationInterface $annotation */
161
            foreach ($annotations as $annotation) {
162
                if (!$this->isSupported($annotation, $action)) {
163
                    continue;
164
                }
165
166
                $name = $this->getAnnotationFieldName($name, $annotation);
167
168
                $this->stackProperty(
169
                    $name,
170
                    $annotations,
171
                    $propertiesWithPosition,
172
                    $propertiesWithoutPosition
173
                );
174
            }
175
        }
176
177
        ksort($propertiesWithPosition);
178
179
        return array_merge(
180
            $propertiesWithPosition,
181
            $propertiesWithoutPosition
182
        );
183
    }
184
185
    /**
186
     * Stack given annotation as a property with or without position.
187
     *
188
     * @param string $name                      Property name.
189
     * @param object $annotation                Annotation.
190
     * @param array  $propertiesWithPosition    Properties with position stack.
191
     * @param array  $propertiesWithoutPosition Properties without position
192
     *                                          stack.
193
     *
194
     * @return void
195
     */
196
    protected function stackProperty(
197
        string $name,
198
        object $annotation,
199
        array &$propertiesWithPosition,
200
        array &$propertiesWithoutPosition,
201
    ): void {
202
        if (!isset($annotation->position)) {
203
            $propertiesWithoutPosition[] = [
204
                'name' => $name,
205
                'annotation' => $annotation,
206
            ];
207
208
            return;
209
        }
210
211
        if (array_key_exists(
212
            $annotation->position,
213
            $propertiesWithPosition
214
        )) {
215
            throw new InvalidArgumentException(
216
                sprintf(
217
                    'Position "%s" is already in use by "%s", try setting a different position for "%s".',
218
                    $annotation->position,
219
                    $propertiesWithPosition[$annotation->position]['name'],
220
                    $name
221
                )
222
            );
223
        }
224
225
        $propertiesWithPosition[$annotation->position] = [
226
            'name' => $name,
227
            'annotation' => $annotation,
228
        ];
229
    }
230
231
}