Completed
Push — master ( beec4b...b0a6d2 )
by Philip
08:54
created

Normalizer   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 98.78%

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 8
dl 0
loc 188
ccs 81
cts 82
cp 0.9878
rs 9.6
c 0
b 0
f 0

5 Methods

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