Completed
Push — standalone ( a45559...3ee90b )
by Philip
04:55
created

Normalizer::normalize()   D

Complexity

Conditions 17
Paths 19

Size

Total Lines 96
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 17.0048

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 96
ccs 38
cts 39
cp 0.9744
rs 4.8361
cc 17
eloc 54
nc 19
nop 4
crap 17.0048

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\Service;
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 Metadata\MetadataFactoryInterface;
12
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
13
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
14
15
class Normalizer
16
{
17
    /**
18
     * @var MetadataFactoryInterface
19
     */
20
    private $metadataFactory;
21
22
    /**
23
     * @var PropertyAccessorInterface
24
     */
25
    private $propertyAccessor;
26
27
    /**
28
     * @var UrlGeneratorInterface
29
     */
30
    private $urlGenerator;
31
32 24
    function __construct(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
33
        MetadataFactoryInterface $metadataFactory,
34
        PropertyAccessorInterface $propertyAccessor,
35
        UrlGeneratorInterface $urlGenerator
36
    ) {
37 24
        $this->metadataFactory = $metadataFactory;
38 24
        $this->propertyAccessor = $propertyAccessor;
39 24
        $this->urlGenerator = $urlGenerator;
40 24
    }
41
42
    /**
43
     * @param mixed    $data
44
     * @param string[] $includes
45
     * @param int      $depth
46
     * @param string   $path
47
     *
48
     * @return array
49
     */
50 24
    public function normalize($data, $includes = [], int $depth = 0, string $path = '')
51
    {
52 24
        if (is_array($data)) {
53 14
            $normalizedData = [];
54 14
            foreach ($data as $datum) {
55 14
                $normalizedData[] = $this->normalize($datum, $includes, $depth + 1, $path);
56
            }
57
58 14
            return $normalizedData;
59
        }
60
61 24
        if (is_object($data)) {
62
63
            /** @var ClassMetadata $classMetadata */
64 24
            $classMetadata = $this->metadataFactory->getMetadataForClass(ClassUtils::getClass($data));
65
66 24
            $normalizedData = [];
67
68 24
            if ($classMetadata->isRestResource() && $classMetadata->hasMethod(Method::GET) && $this->isIncluded(
69
                    $path,
70 24
                    ['_links'],
71
                    $includes
72
                )
73
            ) {
74 2
                $selfLink = $this->urlGenerator->generate(
75 2
                    $classMetadata->namePrefix . '.get',
76 2
                    ['id' => $this->propertyAccessor->getValue($data, $classMetadata->getIdField())],
77 2
                    UrlGeneratorInterface::ABSOLUTE_URL
78
                );
79 2
                $normalizedData['_links'] = [
80
                    'self' => [
81 2
                        'href' => $selfLink
82
                    ]
83
                ];
84
            }
85
86
            /** @var PropertyMetadata $propertyMetadatum */
87 24
            foreach ($classMetadata->propertyMetadata as $propertyMetadatum) {
88
89 24
                if ($propertyMetadatum->isExcluded()) {
90 12
                    continue;
91
                }
92
93 24
                if ($propertyMetadatum->isAssociation()) {
94
95
                    /* Inlude if includable AND it is on include path */
96 18
                    if ($propertyMetadatum->isIncludable() && $this->isIncluded(
97
                            $path,
98 18
                            $propertyMetadatum->getIncludablePaths(),
99
                            $includes
100
                        )
101
                    ) {
102 4
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
103 4
                        if ($propertyMetadatum->isCollection()) {
104
                            /** @var Collection $value */
105 2
                            $value = $value->getValues();
106
                        }
107 4
                        $normalizedData[$propertyMetadatum->name] = $this->normalize(
108
                            $value,
109
                            $includes,
110 4
                            $depth + 1,
111 18
                            $this->appendPath($path, $propertyMetadatum->name)
112
                        );
113
                    }
114
                } else {
115
116
                    /* Inlude if includable is missing OR it is on include path */
117 24
                    if (!$propertyMetadatum->isIncludable() || $this->isIncluded(
118
                            $path,
119 24
                            $propertyMetadatum->getIncludablePaths(),
120
                            $includes
121
                        )
122
                    ) {
123 24
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
124 24
                        if (is_scalar($value) || array_key_exists($propertyMetadatum->getType(), Type::getTypesMap())) {
125 24
                            $normalizedData[$propertyMetadatum->name] = $this->normalizeField(
126
                                $value,
127
                                $propertyMetadatum
128
                            );
129
                        } else {
130 10
                            $normalizedData[$propertyMetadatum->name] = $this->normalize(
131
                                $value,
132
                                $includes,
133 10
                                $depth + 1,
134 24
                                $this->appendPath($path, $propertyMetadatum->name)
135
                            );
136
                        }
137
                    }
138
                }
139
            }
140
141 24
            return $normalizedData;
142
        }
143
144
        return null;
145
    }
146
147 24
    private function isIncluded($currentPath, array $paths, ?array $includes): bool
148
    {
149 24
        if (null === $includes) {
150
            return false;
151
        }
152
153 24
        foreach ($paths as $path) {
154 24
            if (in_array($this->appendPath($currentPath, $path), $includes)) {
155 24
                return true;
156
            }
157
        }
158
159 24
        return false;
160
    }
161
162 24
    private function appendPath($path, $name)
163
    {
164 24
        if (null === $path || '' === $path) {
165 24
            return $name;
166
        }
167
168 4
        return $path . '.' . $name;
169
    }
170
171 24
    private function normalizeField($value, PropertyMetadata $propertyMetadata)
172
    {
173 24
        switch ($propertyMetadata->getType()) {
174 24
            case 'datetime':
175 10
                if (null === $value) {
176 4
                    return null;
177
                }
178
179
                /** @var $value \DateTime */
180 8
                return $value->format('Y-m-d H:i:s');
181
182 24
            case 'date':
183 10
                if (null === $value) {
184 4
                    return null;
185
                }
186
187
                /** @var $value \DateTime */
188 8
                return $value->format('Y-m-d');
189
190 24
            case 'time':
191 10
                if (null === $value) {
192 4
                    return null;
193
                }
194
195
                /** @var $value \DateTime */
196 8
                return $value->format('H:i:s');
197
198
            default:
199 24
                return $value;
200
        }
201
    }
202
}
203