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

Normalizer   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 97.14%

Importance

Changes 0
Metric Value
wmc 32
c 0
b 0
f 0
lcom 1
cbo 8
dl 0
loc 188
ccs 68
cts 70
cp 0.9714
rs 9.6

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
D normalize() 0 96 17
A isIncluded() 0 14 4
A appendPath() 0 8 3
C normalizeField() 0 31 7
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