Passed
Push — develop ( 01015e...01f497 )
by Mathieu
02:26
created

addMapperProperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
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
        $mapper->add($property['name'], ...$settings);
0 ignored issues
show
Bug introduced by
The method add() does not exist on Sonata\AdminBundle\Mapper\MapperInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Sonata\AdminBundle\Mapper\BaseGroupedMapper. Are you sure you never get one of those? ( Ignorable by Annotation )

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

117
        $mapper->/** @scrutinizer ignore-call */ 
118
                 add($property['name'], ...$settings);
Loading history...
118
    }
119
120
    /**
121
     * Load class properties with position annotation.
122
     *
123
     * @param ReflectionClass $class           Entity class.
124
     * @param string|null     $annotationClass Reader annotation class.
125
     * @param string|null     $action          Current action.
126
     *
127
     * @return array
128
     */
129
    private function loadPositionAnnotationFields(
130
        ReflectionClass $class,
131
        ?string $annotationClass,
132
        ?string $action
133
    ): array {
134
        $propertiesAndMethods = array_merge(
135
            $this->getClassPropertiesAnnotations($class, $annotationClass),
136
            $this->getClassMethodsAnnotations($class, $annotationClass)
137
        );
138
139
        $propertiesWithPosition = [];
140
        $propertiesWithoutPosition = [];
141
142
        foreach ($propertiesAndMethods as $name => $annotations) {
143
            /** @var PositionAnnotationInterface $annotation */
144
            foreach ($annotations as $annotation) {
145
                if (!$this->isSupported($annotation, $action)) {
146
                    continue;
147
                }
148
149
                $name = $this->getAnnotationFieldName($name, $annotation);
150
151
                $this->stackProperty(
152
                    $name,
153
                    $annotation,
154
                    $propertiesWithPosition,
155
                    $propertiesWithoutPosition
156
                );
157
            }
158
        }
159
160
        ksort($propertiesWithPosition);
161
162
        return array_merge(
163
            $propertiesWithPosition,
164
            $propertiesWithoutPosition
165
        );
166
    }
167
168
    /**
169
     * Stack given annotation as a property with or without position.
170
     *
171
     * @param string $name                      Property name.
172
     * @param object $annotation                Annotation.
173
     * @param array  $propertiesWithPosition    Properties with position stack.
174
     * @param array  $propertiesWithoutPosition Properties without position
175
     *                                          stack.
176
     *
177
     * @return void
178
     */
179
    protected function stackProperty(
180
        string $name,
181
        object $annotation,
182
        array &$propertiesWithPosition,
183
        array &$propertiesWithoutPosition,
184
    ): void {
185
        if (!isset($annotation->position)) {
186
            $propertiesWithoutPosition[] = [
187
                'name' => $name,
188
                'annotation' => $annotation,
189
            ];
190
191
            return;
192
        }
193
194
        if (array_key_exists(
195
            $annotation->position,
196
            $propertiesWithPosition
197
        )) {
198
            throw new InvalidArgumentException(
199
                sprintf(
200
                    'Position "%s" is already in use by "%s", try setting a different position for "%s".',
201
                    $annotation->position,
202
                    $propertiesWithPosition[$annotation->position]['name'],
203
                    $name
204
                )
205
            );
206
        }
207
208
        $propertiesWithPosition[$annotation->position] = [
209
            'name' => $name,
210
            'annotation' => $annotation,
211
        ];
212
    }
213
214
}