Completed
Push — master ( df27c1...383b45 )
by Pavel
01:37
created

CanHydrate::hydrateRelationships()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.0961

Importance

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