Completed
Push — standalone ( fce7e0...46923f )
by Philip
03:02
created

Normalizer::isIncluded()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.0466

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 6
cts 7
cp 0.8571
rs 9.2
cc 4
eloc 7
nc 4
nop 3
crap 4.0466
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 28
    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 28
        $this->metadataFactory = $metadataFactory;
38 28
        $this->propertyAccessor = $propertyAccessor;
39 28
        $this->urlGenerator = $urlGenerator;
40 28
    }
41
42
    /**
43
     * @param mixed    $data
44
     * @param string[] $includes
45
     * @param int      $depth
46
     * @param string   $path
47
     *
48
     * @return array
49
     */
50 28
    public function normalize($data, $includes = [], int $depth = 0, string $path = '')
51
    {
52 28
        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 28
        if (is_object($data)) {
62
63
            /** @var ClassMetadata $classMetadata */
64 28
            $classMetadata = $this->metadataFactory->getMetadataForClass(ClassUtils::getClass($data));
65
66 28
            $normalizedData = [];
67
68 28
            if ($classMetadata->isRestResource() && $classMetadata->hasMethod(Method::GET) && $this->isIncluded(
69
                    $path,
70 28
                    ['_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 28
            foreach ($classMetadata->propertyMetadata as $propertyMetadatum) {
88
89 28
                if ($propertyMetadatum->isExcluded()) {
90 14
                    continue;
91
                }
92
93 28
                if ($propertyMetadatum->isAssociation()) {
94
95
                    /* Inlude if includable AND it is on include path */
96 22
                    if ($propertyMetadatum->isIncludable() && $this->isIncluded(
97
                            $path,
98 22
                            $propertyMetadatum->getIncludablePaths(),
99
                            $includes
100
                        )
101
                    ) {
102 8
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
103 8
                        if ($propertyMetadatum->isCollection()) {
104
                            /** @var Collection $value */
105 2
                            $value = $value->getValues();
106
                        }
107 8
                        $normalizedData[$propertyMetadatum->name] = $this->normalize(
108
                            $value,
109
                            $includes,
110 8
                            $depth + 1,
111 22
                            $this->appendPath($path, $propertyMetadatum->name)
112
                        );
113
                    }
114
                } else {
115
116
                    /* Inlude if includable is missing OR it is on include path */
117 28
                    if (!$propertyMetadatum->isIncludable() || $this->isIncluded(
118
                            $path,
119 28
                            $propertyMetadatum->getIncludablePaths(),
120
                            $includes
121
                        )
122
                    ) {
123 28
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
124 28
                        if (is_scalar($value) || array_key_exists($propertyMetadatum->getType(), Type::getTypesMap())) {
125 28
                            $normalizedData[$propertyMetadatum->name] = $this->normalizeField(
126
                                $value,
127
                                $propertyMetadatum
128
                            );
129
                        } else {
130 6
                            $normalizedData[$propertyMetadatum->name] = $this->normalize(
131
                                $value,
132
                                $includes,
133 6
                                $depth + 1,
134 28
                                $this->appendPath($path, $propertyMetadatum->name)
135
                            );
136
                        }
137
                    }
138
                }
139
            }
140
141 28
            return $normalizedData;
142
        }
143
144 2
        return null;
145
    }
146
147 28
    private function isIncluded($currentPath, array $paths, ?array $includes): bool
148
    {
149 28
        if (null === $includes) {
150
            return false;
151
        }
152
153 28
        foreach ($paths as $path) {
154 28
            if (in_array($this->appendPath($currentPath, $path), $includes)) {
155 28
                return true;
156
            }
157
        }
158
159 28
        return false;
160
    }
161
162 28
    private function appendPath($path, $name)
163
    {
164 28
        if (null === $path || '' === $path) {
165 28
            return $name;
166
        }
167
168 6
        return $path . '.' . $name;
169
    }
170
171 28
    private function normalizeField($value, PropertyMetadata $propertyMetadata)
172
    {
173 28
        switch ($propertyMetadata->getType()) {
174 28
            case 'datetime':
175 6
                if (null === $value) {
176
                    return null;
177
                }
178
179
                /** @var $value \DateTime */
180 6
                return $value->format('Y-m-d H:i:s');
181
182 28
            case 'date':
183 6
                if (null === $value) {
184
                    return null;
185
                }
186
187
                /** @var $value \DateTime */
188 6
                return $value->format('Y-m-d');
189
190 28
            case 'time':
191 6
                if (null === $value) {
192
                    return null;
193
                }
194
195
                /** @var $value \DateTime */
196 6
                return $value->format('H:i:s');
197
198
            default:
199 28
                return $value;
200
        }
201
    }
202
}
203