Completed
Push — master ( 4339a5...848f8d )
by Pavel
02:38
created

CanHydrateAndValidate::hydrateAttributes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 3
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
crap 3.0261
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->columnNames[$name])) {
88
                throw RestException::unknownAttribute(sprintf('%s.attribute.%s', $scope, $name));
89
            }
90
91 4
            $metadata->reflFields[$name]->setValue($entity, $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
                $metadata->reflFields[$name]->setValue($entity,
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
                $metadata->reflFields[$name]->setValue($entity,
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        $class
152
     * @param        $data
153
     * @param string $scope
154
     *
155
     * @return object
156
     * @throws RestException
157
     * @throws \Doctrine\ORM\ORMException
158
     */
159 2
    protected function hydrateRelationData($class, $data, $scope = 'root')
160
    {
161 2
        if (is_scalar($data)) {
162
            return $this->repository()->getEntityManager()->getReference($class, $data);
163
        }
164
165 2
        if (!is_array($data)) {
166
            throw RestException::missingData($scope);
167
        }
168
169 2
        if (isset($data['id']) && isset($data['type'])) {
170 1
            return $this->repository()->getEntityManager()->getReference($class, $data['id']);
171
        } else {
172 1
            return $this->hydrateData($class, $data, $scope);
173
        }
174
    }
175
}
176