Completed
Push — master ( 658cec...20b116 )
by Philip
06:38
created

RestNormalizer::appendPath()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 2
crap 3
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
        $includes = $context[self::DDR_REST_INCLUDES];
57 62
        $path = $context[self::DDR_REST_PATH];
58 62
        $depth = $context[self::DDR_REST_DEPTH];
59
60 62
        if (is_array($data)) {
61 34
            $normalizedData = [];
62 34
            foreach ($data as $datum) {
63 34
                $normalizedData[] = $this->normalize(
64 34
                    $datum,
65 34
                    $format,
66
                    [
67 34
                        self::DDR_REST_INCLUDES => $includes,
68 34
                        self::DDR_REST_DEPTH    => $depth + 1,
69 34
                        self::DDR_REST_PATH     => $path
70
                    ]
71
                );
72
            }
73
74 34
            return $normalizedData;
75
        }
76
77 62
        if (is_object($data)) {
78
79
            /** @var ClassMetadata $classMetadata */
80 62
            $classMetadata = $this->metadataFactory->getMetadataForClass(ClassUtils::getClass($data));
81
82 62
            $normalizedData = [];
83
84 62
            if ($classMetadata->isRestResource() && $classMetadata->hasMethod(Method::GET) && $this->isIncluded(
85 50
                    $path,
86 50
                    ['_links'],
87 62
                    $includes
88
                )
89
            ) {
90 2
                $selfLink = $this->urlGenerator->generate(
91 2
                    $classMetadata->namePrefix . '.get',
92 2
                    ['id' => $this->propertyAccessor->getValue($data, $classMetadata->getIdField())],
93 2
                    UrlGeneratorInterface::ABSOLUTE_URL
94
                );
95 2
                $normalizedData['_links'] = [
96
                    'self' => [
97 2
                        'href' => $selfLink
98
                    ]
99
                ];
100
            }
101
102
            /** @var PropertyMetadata $propertyMetadatum */
103 62
            foreach ($classMetadata->propertyMetadata as $propertyMetadatum) {
104
105 62
                if ($propertyMetadatum->isExcluded()) {
106 36
                    continue;
107
                }
108
109 62
                if ($propertyMetadatum->isAssociation()) {
110
111
                    /* Inlude if includable AND it is on include path */
112 44
                    if ($propertyMetadatum->isIncludable() && $this->isIncluded(
113 44
                            $path,
114 44
                            $propertyMetadatum->getIncludablePaths(),
115 44
                            $includes
116
                        )
117
                    ) {
118 28
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
119 28
                        if ($propertyMetadatum->isCollection()) {
120
                            /** @var Collection $value */
121 14
                            $value = $value->getValues();
122
                        }
123 28
                        $normalizedData[$propertyMetadatum->name] = $this->normalize(
124 28
                            $value,
125 28
                            $format,
126
                            [
127 28
                                self::DDR_REST_INCLUDES => $includes,
128 28
                                self::DDR_REST_DEPTH    => $depth + 1,
129 44
                                self::DDR_REST_PATH     => $this->appendPath($path, $propertyMetadatum->name)
130
                            ]
131
                        );
132
                    }
133
                } else {
134
135
                    /* Inlude if includable is missing OR it is on include path */
136 62
                    if (!$propertyMetadatum->isIncludable() || $this->isIncluded(
137 18
                            $path,
138 18
                            $propertyMetadatum->getIncludablePaths(),
139 62
                            $includes
140
                        )
141
                    ) {
142 62
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
143 62
                        if (is_scalar($value) || array_key_exists($propertyMetadatum->getType(), Type::getTypesMap())) {
144 62
                            $normalizedData[$propertyMetadatum->name] = $this->normalizeField(
145 62
                                $value,
146 62
                                $propertyMetadatum
147
                            );
148
                        } else {
149 28
                            $normalizedData[$propertyMetadatum->name] = $this->normalize(
150 28
                                $value,
151 28
                                $format,
152
                                [
153 28
                                    self::DDR_REST_INCLUDES => $includes,
154 28
                                    self::DDR_REST_DEPTH    => $depth + 1,
155 62
                                    self::DDR_REST_PATH     => $this->appendPath($path, $propertyMetadatum->name)
156
                                ]
157
                            );
158
                        }
159
                    }
160
                }
161
            }
162
163 62
            return $normalizedData;
164
        }
165
166 22
        return $data;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 62
    public function supportsNormalization($data, $format = null)
173
    {
174 62
        if ('json' === $format) {
175 62
            return true;
176
        }
177
178
        return false;
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 62
    public function hasCacheableSupportsMethod(): bool
185
    {
186 62
        return true;
187
    }
188
189 50
    private function isIncluded($currentPath, array $paths, ?array $includes): bool
190
    {
191 50
        if (null === $includes) {
192
            return false;
193
        }
194
195 50
        foreach ($paths as $path) {
196 50
            if (in_array($this->appendPath($currentPath, $path), $includes)) {
197 50
                return true;
198
            }
199
        }
200
201 50
        return false;
202
    }
203
204 50
    private function appendPath($path, $name)
205
    {
206 50
        if (null === $path || '' === $path) {
207 50
            return $name;
208
        }
209
210 26
        return $path . '.' . $name;
211
    }
212
213 62
    private function normalizeField($value, PropertyMetadata $propertyMetadata)
214
    {
215 62
        switch ($propertyMetadata->getType()) {
216 62
            case 'datetime':
217 8
                if (null === $value) {
218 2
                    return null;
219
                }
220
221
                /** @var $value \DateTime */
222 6
                return $value->format('Y-m-d H:i:s');
223
224 62
            case 'date':
225 8
                if (null === $value) {
226 2
                    return null;
227
                }
228
229
                /** @var $value \DateTime */
230 6
                return $value->format('Y-m-d');
231
232 62
            case 'time':
233 8
                if (null === $value) {
234 2
                    return null;
235
                }
236
237
                /** @var $value \DateTime */
238 6
                return $value->format('H:i:s');
239
240
            default:
241 62
                return $value;
242
        }
243
    }
244
}
245