Completed
Push — master ( a2ccb4...2afc29 )
by Philip
05:08
created

Service/Normalizer.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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