Passed
Push — develop ( 75c5d8...34a9e9 )
by Mathieu
03:02
created

configureReaderFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 14
rs 10
cc 1
nc 1
nop 4
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\AssociationFieldInterface;
9
use KunicMarko\SonataAnnotationBundle\Annotation\PositionAnnotationInterface;
10
use ReflectionClass;
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
17
/**
18
 * Field configuration reader.
19
 */
20
class AbstractFieldConfigurationReader extends AbstractReader
21
{
22
23
    /**
24
     * Associated annotation class.
25
     *
26
     * @var string|null
27
     */
28
    protected ?string $annotationClass;
29
30
    /**
31
     * @param Reader  $annotationReader Doctrine annotation reader.
32
     * @param ?string $annotationClass  Associated annotation class.
33
     */
34
    public function __construct(
35
        Reader $annotationReader,
36
        ?string $annotationClass = null
37
    ) {
38
        parent::__construct($annotationReader);
39
        $this->annotationClass = $annotationClass;
40
    }
41
42
    /**
43
     * Build list field configurations.
44
     *
45
     * @param ReflectionClass $class  Entity class.
46
     * @param MapperInterface $mapper Admin mapper.
47
     *
48
     * @return void
49
     */
50
    public function configureFields(
51
        ReflectionClass $class,
52
        MapperInterface $mapper
53
    ): void {
54
        $this->configureReaderFields(
55
            $class,
56
            $mapper,
57
            $this->annotationClass
58
        );
59
    }
60
61
    /**
62
     * Configure fields with positions.
63
     *
64
     * @param ReflectionClass $class           Read entity class.
65
     * @param MapperInterface $mapper          Admin mapper.
66
     * @param string|null     $annotationClass Filtered annotation class.
67
     * @param string|null     $action          Current action.
68
     *
69
     * @return void
70
     */
71
    protected function configureReaderFields(
72
        ReflectionClass $class,
73
        MapperInterface $mapper,
74
        ?string $annotationClass = null,
75
        ?string $action = null
76
    ): void {
77
        $fields = $this->loadPositionAnnotationFields(
78
            $class,
79
            $annotationClass,
80
            $action
81
        );
82
        array_walk(
83
            $fields,
84
            fn(array $field) => $this->addMapperProperty($mapper, $field)
85
        );
86
    }
87
88
    /**
89
     * Check if given annotation is supported.
90
     *
91
     * @param object      $annotation Annotation.
92
     * @param string|null $action     Current action.
93
     *
94
     * @return bool
95
     */
96
    protected function isSupported(object $annotation, ?string $action): bool
97
    {
98
        return !$annotation instanceof ActionAnnotationInterface
99
            || $action === null
100
            || !isset($annotation->action)
101
            || $annotation->action === $action;
102
    }
103
104
    /**
105
     * Add a property to the given mapper.
106
     *
107
     * @param MapperInterface $mapper   Admin mapper.
108
     * @param array           $property Property information.
109
     *
110
     * @return void
111
     */
112
    private function addMapperProperty(
113
        MapperInterface $mapper,
114
        array $property
115
    ): void {
116
        $settings = $property['annotation']->getSettings();
117
118
        if ($mapper instanceof DatagridMapper
119
            || $mapper instanceof FormMapper
120
        ) {
121
            $mapper->add(
122
                $property['name'],
123
                array_shift($settings),
124
                array_shift($settings) ?: [],
125
                array_shift($settings) ?: [],
126
            );
127
        } elseif ($mapper instanceof ListMapper
128
            || $mapper instanceof ShowMapper
129
        ) {
130
            $mapper->add(
131
                $property['name'],
132
                array_shift($settings),
133
                array_shift($settings) ?: [],
134
            );
135
        }
136
    }
137
138
    /**
139
     * Load class properties with position annotation.
140
     *
141
     * @param ReflectionClass $class           Entity class.
142
     * @param string|null     $annotationClass Reader annotation class.
143
     * @param string|null     $action          Current action.
144
     *
145
     * @return array
146
     */
147
    private function loadPositionAnnotationFields(
148
        ReflectionClass $class,
149
        ?string $annotationClass,
150
        ?string $action
151
    ): array {
152
        $propertiesAndMethods = array_merge(
153
            $this->getClassPropertiesAnnotations($class, $annotationClass),
154
            $this->getClassMethodsAnnotations($class, $annotationClass)
155
        );
156
157
        $propertiesWithPosition = [];
158
        $propertiesWithoutPosition = [];
159
160
        foreach ($propertiesAndMethods as $name => $annotations) {
161
            /** @var PositionAnnotationInterface $annotation */
162
            foreach ($annotations as $annotation) {
163
                if (!$this->isSupported($annotation, $action)) {
164
                    continue;
165
                }
166
167
                if ($annotation instanceof AssociationFieldInterface) {
168
                    $name = sprintf('%s.%s', $name, $annotation->field);
169
                }
170
171
                if (!isset($annotation->position)) {
172
                    $propertiesWithoutPosition[] = [
173
                        'name' => $name,
174
                        'annotation' => $annotation,
175
                    ];
176
                    continue;
177
                }
178
179
                if (array_key_exists(
180
                    $annotation->position,
181
                    $propertiesWithPosition
182
                )) {
183
                    throw new InvalidArgumentException(
184
                        sprintf(
185
                            'Position "%s" is already in use by "%s", try setting a different position for "%s".',
186
                            $annotation->position,
187
                            $propertiesWithPosition[$annotation->position]['name'],
188
                            $name
189
                        )
190
                    );
191
                }
192
193
                $propertiesWithPosition[$annotation->position] = [
194
                    'name' => $name,
195
                    'annotation' => $annotation,
196
                ];
197
            }
198
        }
199
200
        ksort($propertiesWithPosition);
201
202
        return array_merge(
203
            $propertiesWithPosition,
204
            $propertiesWithoutPosition
205
        );
206
    }
207
208
}