Completed
Push — master ( 8f7574...cf93e2 )
by Philip
10:37
created

RestNormalizer::normalize()   F

Complexity

Conditions 20
Paths 22

Size

Total Lines 126

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 64
CRAP Score 20.0359

Importance

Changes 0
Metric Value
dl 0
loc 126
ccs 64
cts 67
cp 0.9552
rs 3.3333
c 0
b 0
f 0
cc 20
nc 22
nop 3
crap 20.0359

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Dontdrinkandroot\RestBundle\Serializer;
4
5
use Doctrine\Common\Collections\Collection;
6
use Doctrine\Common\Util\ClassUtils;
7
use Doctrine\DBAL\Types\Type;
8
use Dontdrinkandroot\RestBundle\Metadata\Annotation\Method;
9
use Dontdrinkandroot\RestBundle\Metadata\ClassMetadata;
10
use Dontdrinkandroot\RestBundle\Metadata\PropertyMetadata;
11
use Dontdrinkandroot\RestBundle\Metadata\RestMetadataFactory;
12
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
13
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
14
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
15
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
16
17
/**
18
 * @author Philip Washington Sorst <[email protected]>
19
 */
20
class RestNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
21
{
22
    const DDR_REST_INCLUDES = 'ddrRestIncludes';
23
    const DDR_REST_PATH = 'ddrRestPath';
24
    const DDR_REST_DEPTH = 'ddrRestDepth';
25
26
    /**
27
     * @var RestMetadataFactory
28
     */
29
    private $metadataFactory;
30
31
    /**
32
     * @var PropertyAccessorInterface
33
     */
34
    private $propertyAccessor;
35
36
    /**
37
     * @var UrlGeneratorInterface
38
     */
39
    private $urlGenerator;
40
41 80
    public function __construct(
42
        RestMetadataFactory $metadataFactory,
43
        PropertyAccessorInterface $propertyAccessor,
44
        UrlGeneratorInterface $urlGenerator
45
    ) {
46 80
        $this->metadataFactory = $metadataFactory;
47 80
        $this->propertyAccessor = $propertyAccessor;
48 80
        $this->urlGenerator = $urlGenerator;
49 80
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54 62
    public function normalize($data, $format = null, array $context = [])
55
    {
56 62
        if (!array_key_exists(self::DDR_REST_INCLUDES, $context)) {
57
            throw new \LogicException('Includes missing');
58
        }
59
60 62
        if (!array_key_exists(self::DDR_REST_PATH, $context)) {
61
            throw new \LogicException('Path missing');
62
        }
63
64 62
        if (!array_key_exists(self::DDR_REST_DEPTH, $context)) {
65
            throw new \LogicException('Depth missing');
66
        }
67
68 62
        $includes = $context[self::DDR_REST_INCLUDES];
69 62
        $path = $context[self::DDR_REST_PATH];
70 62
        $depth = $context[self::DDR_REST_DEPTH];
71
72 62
        if (is_array($data)) {
73 34
            $normalizedData = [];
74 34
            foreach ($data as $datum) {
75 34
                $normalizedData[] = $this->normalize(
76 34
                    $datum,
77 34
                    $format,
78
                    [
79 34
                        self::DDR_REST_INCLUDES => $includes,
80 34
                        self::DDR_REST_DEPTH    => $depth + 1,
81 34
                        self::DDR_REST_PATH     => $path
82
                    ]
83
                );
84
            }
85
86 34
            return $normalizedData;
87
        }
88
89 62
        if (is_object($data)) {
90
91
            /** @var ClassMetadata $classMetadata */
92 62
            $classMetadata = $this->metadataFactory->getMetadataForClass(ClassUtils::getClass($data));
93
94 62
            $normalizedData = [];
95
96 62
            if ($classMetadata->isRestResource() && $classMetadata->hasMethod(Method::GET) && $this->isIncluded(
97 50
                    $path,
98 50
                    ['_links'],
99 62
                    $includes
100
                )
101
            ) {
102 2
                $selfLink = $this->urlGenerator->generate(
103 2
                    $classMetadata->namePrefix . '.get',
104 2
                    ['id' => $this->propertyAccessor->getValue($data, $classMetadata->getIdField())],
105 2
                    UrlGeneratorInterface::ABSOLUTE_URL
106
                );
107 2
                $normalizedData['_links'] = [
108
                    'self' => [
109 2
                        'href' => $selfLink
110
                    ]
111
                ];
112
            }
113
114
            /** @var PropertyMetadata $propertyMetadatum */
115 62
            foreach ($classMetadata->propertyMetadata as $propertyMetadatum) {
116
117 62
                if ($propertyMetadatum->isExcluded()) {
118 36
                    continue;
119
                }
120
121 62
                if ($propertyMetadatum->isAssociation()) {
122
123
                    /* Inlude if includable AND it is on include path */
124 44
                    if ($propertyMetadatum->isIncludable() && $this->isIncluded(
125 44
                            $path,
126 44
                            $propertyMetadatum->getIncludablePaths(),
127 44
                            $includes
128
                        )
129
                    ) {
130 28
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
131 28
                        if ($propertyMetadatum->isCollection()) {
132
                            /** @var Collection $value */
133 14
                            $value = $value->getValues();
134
                        }
135 28
                        $normalizedData[$propertyMetadatum->name] = $this->normalize(
136 28
                            $value,
137 28
                            $format,
138
                            [
139 28
                                self::DDR_REST_INCLUDES => $includes,
140 28
                                self::DDR_REST_DEPTH    => $depth + 1,
141 44
                                self::DDR_REST_PATH     => $this->appendPath($path, $propertyMetadatum->name)
142
                            ]
143
                        );
144
                    }
145
                } else {
146
147
                    /* Inlude if includable is missing OR it is on include path */
148 62
                    if (!$propertyMetadatum->isIncludable() || $this->isIncluded(
149 18
                            $path,
150 18
                            $propertyMetadatum->getIncludablePaths(),
151 62
                            $includes
152
                        )
153
                    ) {
154 62
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
155 62
                        if (is_scalar($value) || array_key_exists($propertyMetadatum->getType(), Type::getTypesMap())) {
156 62
                            $normalizedData[$propertyMetadatum->name] = $this->normalizeField(
157 62
                                $value,
158 62
                                $propertyMetadatum
159
                            );
160
                        } else {
161 28
                            $normalizedData[$propertyMetadatum->name] = $this->normalize(
162 28
                                $value,
163 28
                                $format,
164
                                [
165 28
                                    self::DDR_REST_INCLUDES => $includes,
166 28
                                    self::DDR_REST_DEPTH    => $depth + 1,
167 62
                                    self::DDR_REST_PATH     => $this->appendPath($path, $propertyMetadatum->name)
168
                                ]
169
                            );
170
                        }
171
                    }
172
                }
173
            }
174
175 62
            return $normalizedData;
176
        }
177
178 22
        return $data;
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 62
    public function supportsNormalization($data, $format = null)
185
    {
186 62
        if ('json' === $format) {
187 62
            return true;
188
        }
189
190
        return false;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196 62
    public function hasCacheableSupportsMethod(): bool
197
    {
198 62
        return true;
199
    }
200
201 50
    private function isIncluded($currentPath, array $paths, ?array $includes): bool
202
    {
203 50
        if (null === $includes) {
204
            return false;
205
        }
206
207 50
        foreach ($paths as $path) {
208 50
            if (in_array($this->appendPath($currentPath, $path), $includes)) {
209 50
                return true;
210
            }
211
        }
212
213 50
        return false;
214
    }
215
216 50
    private function appendPath($path, $name)
217
    {
218 50
        if (null === $path || '' === $path) {
219 50
            return $name;
220
        }
221
222 26
        return $path . '.' . $name;
223
    }
224
225 62
    private function normalizeField($value, PropertyMetadata $propertyMetadata)
226
    {
227 62
        switch ($propertyMetadata->getType()) {
228
229 62
            case Type::DATETIME:
230 8
                if (null === $value) {
231 2
                    return null;
232
                }
233
234
                /** @var $value \DateTime */
235 6
                return $value->format('Y-m-d H:i:s');
236
237 62
            case Type::DATE:
238 8
                if (null === $value) {
239 2
                    return null;
240
                }
241
242
                /** @var $value \DateTime */
243 6
                return $value->format('Y-m-d');
244
245 62
            case Type::TIME:
246 8
                if (null === $value) {
247 2
                    return null;
248
                }
249
250
                /** @var $value \DateTime */
251 6
                return $value->format('H:i:s');
252
253
            default:
254 62
                return $value;
255
        }
256
    }
257
}
258