Completed
Push — master ( 658cec...20b116 )
by Philip
06:38
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
/**
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 80
    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...
36
        MetadataFactoryInterface $metadataFactory,
37
        PropertyAccessorInterface $propertyAccessor,
38
        UrlGeneratorInterface $urlGenerator
39
    ) {
40 80
        $this->metadataFactory = $metadataFactory;
41 80
        $this->propertyAccessor = $propertyAccessor;
42 80
        $this->urlGenerator = $urlGenerator;
43 80
    }
44
45
    /**
46
     * @param mixed    $data
47
     * @param string[] $includes
48
     * @param int      $depth
49
     * @param string   $path
50
     *
51
     * @return array
52
     */
53 62
    public function normalize($data, $includes = [], int $depth = 0, string $path = '')
54
    {
55 62
        if (is_array($data)) {
56 34
            $normalizedData = [];
57 34
            foreach ($data as $datum) {
58 34
                $normalizedData[] = $this->normalize($datum, $includes, $depth + 1, $path);
59
            }
60
61 34
            return $normalizedData;
62
        }
63
64 62
        if (is_object($data)) {
65
66
            /** @var ClassMetadata $classMetadata */
67 62
            $classMetadata = $this->metadataFactory->getMetadataForClass(ClassUtils::getClass($data));
68
69 62
            $normalizedData = [];
70
71 62
            if ($classMetadata->isRestResource() && $classMetadata->hasMethod(Method::GET) && $this->isIncluded(
72 50
                    $path,
73 50
                    ['_links'],
74 62
                    $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 62
            foreach ($classMetadata->propertyMetadata as $propertyMetadatum) {
91
92 62
                if ($propertyMetadatum->isExcluded()) {
93 36
                    continue;
94
                }
95
96 62
                if ($propertyMetadatum->isAssociation()) {
97
98
                    /* Inlude if includable AND it is on include path */
99 44
                    if ($propertyMetadatum->isIncludable() && $this->isIncluded(
100 44
                            $path,
101 44
                            $propertyMetadatum->getIncludablePaths(),
102 44
                            $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 44
                            $this->appendPath($path, $propertyMetadatum->name)
115
                        );
116
                    }
117
                } else {
118
119
                    /* Inlude if includable is missing OR it is on include path */
120 62
                    if (!$propertyMetadatum->isIncludable() || $this->isIncluded(
121 18
                            $path,
122 18
                            $propertyMetadatum->getIncludablePaths(),
123 62
                            $includes
124
                        )
125
                    ) {
126 62
                        $value = $this->propertyAccessor->getValue($data, $propertyMetadatum->name);
127 62
                        if (is_scalar($value) || array_key_exists($propertyMetadatum->getType(), Type::getTypesMap())) {
128 62
                            $normalizedData[$propertyMetadatum->name] = $this->normalizeField(
129 62
                                $value,
130 62
                                $propertyMetadatum
131
                            );
132
                        } else {
133 28
                            $normalizedData[$propertyMetadatum->name] = $this->normalize(
134 28
                                $value,
135 28
                                $includes,
136 28
                                $depth + 1,
137 62
                                $this->appendPath($path, $propertyMetadatum->name)
138
                            );
139
                        }
140
                    }
141
                }
142
            }
143
144 62
            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 62
    private function normalizeField($value, PropertyMetadata $propertyMetadata)
175
    {
176 62
        switch ($propertyMetadata->getType()) {
177 62
            case 'datetime':
178 8
                if (null === $value) {
179 2
                    return null;
180
                }
181
182
                /** @var $value \DateTime */
183 6
                return $value->format('Y-m-d H:i:s');
184
185 62
            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 62
            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 62
                return $value;
203
        }
204
    }
205
}
206