Completed
Push — master ( 3a7cc6...2fd33a )
by Jacob
8s
created

Hydrator::normalize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 17
rs 9.4285
cc 1
eloc 8
nc 1
nop 3
1
<?php
2
3
namespace As3\Modlr\Persister\MongoDb;
4
5
use As3\Modlr\Metadata\EntityMetadata;
6
use As3\Modlr\Metadata\RelationshipMetadata;
7
use As3\Modlr\Persister\PersisterException;
8
use As3\Modlr\Store\Store;
9
use Doctrine\MongoDB\Cursor;
10
11
/**
12
 * Hydrates raw, MongoDB array results into RecordSet instances.
13
 *
14
 * @author Jacob Bare <[email protected]>
15
 */
16
final class Hydrator
17
{
18
    /**
19
     * Creates a cursor record set object from a MongoDB cursor.
20
     *
21
     * @param   EntityMetadata  $metadata
22
     * @param   Cursor          $cursor
23
     * @param   Store           $store
24
     * @return  CursorRecordSet
25
     */
26
    public function createCursorRecordSet(EntityMetadata $metadata, Cursor $cursor, Store $store)
27
    {
28
        return new CursorRecordSet($metadata, $cursor, $store, $this);
29
    }
30
31
    /**
32
     * Extracts the model type from a raw MongoDB record.
33
     *
34
     * @param   EntityMetadata  $metadata
35
     * @param   array           $record
36
     * @return  string
37
     * @throws  PersisterException
38
     */
39
    public function extractType(EntityMetadata $metadata, array $record)
40
    {
41
        if (false === $metadata->isPolymorphic()) {
42
            return $metadata->type;
43
        }
44
        return $this->extractField(Persister::POLYMORPHIC_KEY, $record);
45
    }
46
47
    /**
48
     * Processes a raw MongoDB record and normalizes it into a standard array.
49
     *
50
     * @param   EntityMetadata  $metadata
51
     * @param   array           $record
52
     * @param   Store           $store
53
     * @return  array
54
     */
55
    public function normalize(EntityMetadata $metadata, array $record, Store $store)
56
    {
57
        // Get the identifier and model type value from the raw record.
58
        list($identifier, $type) = $this->extractIdAndType($metadata, $record);
59
60
        // Reload the metadata in case a polymorphic type was found.
61
        $metadata = $store->getMetadataForType($type);
62
63
        // Convert relationships to the proper format.
64
        $record = $this->convertRelationships($metadata, $record);
65
66
        return [
67
            'identifier'    => (String) $identifier,
68
            'type'          => $type,
69
            'properties'    => $record,
70
        ];
71
    }
72
73
    /**
74
     * Converts the relationships on a raw result to the proper format.
75
     *
76
     * @param   EntityMetadata  $metadata
77
     * @param   array           &$data
78
     * @return  array
79
     */
80
    private function convertRelationships(EntityMetadata $metadata, array $data)
81
    {
82
        foreach ($metadata->getRelationships() as $key => $relMeta) {
83
            if (!isset($data[$key])) {
84
                continue;
85
            }
86
            if (true === $relMeta->isMany() && !is_array($data[$key])) {
87
                throw PersisterException::badRequest(sprintf('Relationship key "%s" is a reference many. Expected record data type of array, "%s" found on model "%s" for identifier "%s"', $key, gettype($data[$key]), $type, $identifier));
88
            }
89
            $references = $relMeta->isOne() ? [$data[$key]] : $data[$key];
90
91
            $extracted = [];
92
            foreach ($references as $reference) {
93
                $extracted[] =  $this->extractRelationship($relMeta, $reference);
94
            }
95
            $data[$key] = $relMeta->isOne() ? reset($extracted) : $extracted;
96
        }
97
        return $data;
98
    }
99
100
    /**
101
     * Extracts a root field from a raw MongoDB result.
102
     *
103
     * @param   string  $key
104
     * @param   array   $data
105
     * @return  mixed
106
     * @throws  PersisterException
107
     */
108
    private function extractField($key, array $data)
109
    {
110
        if (!isset($data[$key])) {
111
            throw PersisterException::badRequest(sprintf('Unable to extract a field value. The "%s" key was not found.', $key));
112
        }
113
        return $data[$key];
114
    }
115
116
    /**
117
     * Extracts the identifier and model type from a raw result and removes them from the source data.
118
     * Returns as a tuple.
119
     *
120
     * @param   EntityMetadata  $metadata
121
     * @param   array           &$data
122
     * @return  array
123
     * @throws  PersisterException
124
     */
125
    private function extractIdAndType(EntityMetadata $metadata, array &$data)
126
    {
127
        $identifier = $this->extractField(Persister::IDENTIFIER_KEY, $data);
128
        unset($data[Persister::IDENTIFIER_KEY]);
129
130
        $key = Persister::POLYMORPHIC_KEY;
131
        $type = $this->extractType($metadata, $data);
132
        if (isset($data[$key])) {
133
            unset($data[$key]);
134
        }
135
        return [$identifier, $type];
136
    }
137
138
    /**
139
     * Extracts a standard relationship array that the store expects from a raw MongoDB reference value.
140
     *
141
     * @param   RelationshipMetadata    $relMeta
142
     * @param   mixed                   $reference
143
     * @return  array
144
     * @throws  RuntimeException        If the relationship could not be extracted.
145
     */
146
    private function extractRelationship(RelationshipMetadata $relMeta, $reference)
147
    {
148
        $simple  = false === $relMeta->isPolymorphic();
149
        $idKey   = Persister::IDENTIFIER_KEY;
150
        $typeKey = Persister::POLYMORPHIC_KEY;
151
152
        if (true === $simple && is_array($reference) && isset($reference[$idKey])) {
153
            return [
154
                'id'    => $reference[$idKey],
155
                'type'  => $relMeta->getEntityType(),
156
            ];
157
        }
158
159
        if (true === $simple && !is_array($reference)) {
160
            return [
161
                'id'    => $reference,
162
                'type'  => $relMeta->getEntityType(),
163
            ];
164
        }
165
166
        if (false === $simple && is_array($reference) && isset($reference[$idKey]) && isset($reference[$typeKey])) {
167
            return [
168
                'id'    => $reference[$idKey],
169
                'type'  => $reference[$typeKey],
170
            ];
171
        }
172
        throw PersisterException::badRequest('Unable to extract a reference id.');
173
    }
174
}
175