Completed
Push — master ( 848f8d...adcfba )
by Pavel
01:34
created

CanHydrateAndValidate::setProperty()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 1
nop 3
dl 0
loc 8
ccs 4
cts 5
cp 0.8
crap 2.032
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Pz\Doctrine\Rest\Traits;
2
3
use Doctrine\Common\Collections\ArrayCollection;
4
use Doctrine\ORM\Mapping\ClassMetadataInfo;
5
use Pz\Doctrine\Rest\Exceptions\RestException;
6
use Pz\Doctrine\Rest\RestRepository;
7
use Pz\Doctrine\Rest\RestResponse;
8
use Symfony\Component\HttpFoundation\Response;
9
use Symfony\Component\Validator\ConstraintViolation;
10
use Symfony\Component\Validator\Validation;
11
12
trait CanHydrateAndValidate
13
{
14
    /**
15
     * @return RestRepository
16
     */
17
    abstract public function repository();
18
19
    /**
20
     * @param object $entity
21
     * @param string $scope
22
     *
23
     * @return mixed
24
     * @throws RestException
25
     */
26 4
    protected function validateEntity($entity, $scope)
27
    {
28 4
        $errors = Validation::createValidatorBuilder()
29 4
            ->enableAnnotationMapping()
30 4
            ->getValidator()
31 4
            ->validate($entity);
32
33 4
        if (count($errors) > 0) {
34 1
            $exception = RestException::createUnprocessable();
35
36
            /** @var ConstraintViolation $error */
37 1
            foreach ($errors as $error) {
38 1
                $source = ['pointer' => $scope, 'field' => $error->getPropertyPath()];
39 1
                $exception->error('validation', $source, $error->getMessage());
40
            }
41
42 1
            throw $exception;
43
        }
44
45 3
        return $entity;
46
    }
47
48
    /**
49
     * @param string|object $entity
50
     * @param array         $data
51
     * @param string        $scope
52
     *
53
     * @return object
54
     * @throws RestException
55
     */
56 4
    protected function hydrateData($entity, array $data, $scope = 'root')
57
    {
58 4
        $entity = is_object($entity) ? $entity : new $entity;
59
60 4
        if (!isset($data['attributes']) || !is_array($data['attributes'])) {
61
            throw RestException::missingAttributes($scope);
62
        }
63
64 4
        $entity = $this->hydrateAttributes($entity, $data['attributes'], $scope);
65
66 4
        if (isset($data['relationships']) && is_array($data['relationships'])) {
67 1
            $entity = $this->hydrateRelationships($entity, $data['relationships'], $scope);
68
        }
69
70 4
        $this->validateEntity($entity, $scope);
71
72 3
        return $entity;
73
    }
74
75
    /**
76
     * @param        $entity
77
     * @param array  $attributes
78
     * @param string $scope
79
     *
80
     * @return mixed
81
     * @throws RestException
82
     */
83 4
    protected function hydrateAttributes($entity, array $attributes, $scope = 'root')
84
    {
85 4
        $metadata = $this->repository()->getClassMetadata();
86 4
        foreach ($attributes as $name => $value) {
87 4
            if (!isset($metadata->reflFields[$name])) {
88
                throw RestException::unknownAttribute(sprintf('%s.attribute.%s', $scope, $name));
89
            }
90
91 4
            $this->setProperty($entity, $name, $value);
92
        }
93
94 4
        return $entity;
95
    }
96
97
    /**
98
     * @param       $entity
99
     * @param array $relationships
100
     * @param       $scope
101
     *
102
     * @return mixed
103
     * @throws RestException
104
     */
105 1
    protected function hydrateRelationships($entity, array $relationships, $scope)
106
    {
107 1
        $metadata = $this->repository()->getClassMetadata();
108
109 1
        foreach ($relationships as $name => $data) {
110 1
            $relationScope = sprintf('%s.relation.%s', $scope, $name);
111
112 1
            if (!isset($metadata->associationMappings[$name])) {
113
                throw RestException::unknownRelation($relationScope);
114
            }
115
116 1
            $mapping = $metadata->associationMappings[$name];
117 1
            $mappingClass = $mapping['targetEntity'];
118
119 1
            if (!isset($data['data'])) {
120
                throw RestException::missingData($relationScope);
121
            }
122
123 1
            if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
124 1
                $this->setProperty($entity, $name,
125 1
                    $this->hydrateRelationData($mappingClass, $data['data'], $relationScope)
126
                );
127
            }
128
129 1
            if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY])) {
130
                if (!is_array($data['data'])) {
131
                    throw RestException::missingData($relationScope);
132
                }
133
134
                $this->setProperty($entity, $name,
135
                    new ArrayCollection(array_map(
136
                        function($data, $index) use ($mappingClass, $relationScope) {
137
                            return $this->hydrateRelationData(
138
                                $mappingClass, $data, sprintf('%s.%s', $relationScope, $index)
139
                            );
140
                        },
141 1
                        $data['data']
142
                    ))
143
                );
144
            }
145
        }
146
147 1
        return $entity;
148
    }
149
150
    /**
151
     * @param object $entity
152
     * @param string $name
153
     * @param mixed  $value
154
     *
155
     * @return object
156
     * @throws RestException
157
     */
158 4
    protected function setProperty($entity, $name, $value)
159
    {
160 4
        $setter = 'set' . ucfirst($name);
161 4
        if (!method_exists($entity, $setter)) {
162
            throw RestException::createUnprocessable(sprintf('Setter not found for entity'));
163
        }
164
165 4
        return $entity->$setter($value);
166
    }
167
168
    /**
169
     * @param        $class
170
     * @param        $data
171
     * @param string $scope
172
     *
173
     * @return object
174
     * @throws RestException
175
     * @throws \Doctrine\ORM\ORMException
176
     */
177 2
    protected function hydrateRelationData($class, $data, $scope = 'root')
178
    {
179 2
        if (is_scalar($data)) {
180
            return $this->repository()->getEntityManager()->getReference($class, $data);
181
        }
182
183 2
        if (!is_array($data)) {
184
            throw RestException::missingData($scope);
185
        }
186
187 2
        if (isset($data['id']) && isset($data['type'])) {
188 1
            return $this->repository()->getEntityManager()->getReference($class, $data['id']);
189
        } else {
190 1
            return $this->hydrateData($class, $data, $scope);
191
        }
192
    }
193
}
194