Completed
Push — master ( 32ac03...5fc317 )
by Simonas
03:33
created

Converter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Result;
13
14
use ONGR\ElasticsearchBundle\Document\DocumentInterface;
15
use ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
16
use ONGR\ElasticsearchBundle\Service\Repository;
17
18
/**
19
 * This class converts array to document object.
20
 */
21
class Converter
22
{
23
    /**
24
     * @var MetadataCollector
25
     */
26
    private $metadataCollector;
27
28
    /**
29
     * Constructor.
30
     *
31
     * @param MetadataCollector $metadataCollector
32
     */
33
    public function __construct($metadataCollector)
34
    {
35
        $this->metadataCollector = $metadataCollector;
36
    }
37
38
    /**
39
     * Converts raw array to document.
40
     *
41
     * @param array      $rawData
42
     * @param Repository $repository
43
     *
44
     * @return DocumentInterface
45
     *
46
     * @throws \LogicException
47
     */
48
    public function convertToDocument($rawData, Repository $repository)
49
    {
50
        $types = $this->metadataCollector->getMappings($repository->getManager()->getConfig()['mappings']);
51
52
        if (isset($types[$rawData['_type']])) {
53
            $metadata = $types[$rawData['_type']];
54
        } else {
55
            throw new \LogicException("Got document of unknown type '{$rawData['_type']}'.");
56
        }
57
58
        switch (true) {
59
            case isset($rawData['_source']):
60
                $rawData = array_merge($rawData, $rawData['_source']);
61
                break;
62
            case isset($rawData['fields']):
63
                $rawData = array_merge($rawData, $rawData['fields']);
64
                break;
65
            default:
66
                // Do nothing.
67
                break;
68
        }
69
70
        /** @var DocumentInterface $object */
71
        $object = $this->assignArrayToObject($rawData, new $metadata['namespace'](), $metadata['aliases']);
72
73
        return $object;
74
    }
75
76
    /**
77
     * Assigns all properties to object.
78
     *
79
     * @param array            $array
80
     * @param \ReflectionClass $object
81
     * @param array            $aliases
82
     *
83
     * @return object
84
     */
85
    public function assignArrayToObject(array $array, $object, array $aliases)
86
    {
87
        foreach ($array as $name => $value) {
88
            if (!isset($aliases[$name])) {
89
                continue;
90
            }
91
92
            if (isset($aliases[$name]['type'])) {
93
                switch ($aliases[$name]['type']) {
94
                    case 'date':
95
                        $value = \DateTime::createFromFormat(
96
                            isset($aliases[$name]['format']) ? $aliases[$name]['format'] : \DateTime::ISO8601,
97
                            $value
98
                        );
99
                        break;
100
                    case 'object':
101
                    case 'nested':
102
                        if ($aliases[$name]['multiple']) {
103
                            $value = new ObjectIterator($this, $value, $aliases[$name]);
104
                        } else {
105
                            if (!isset($value)) {
106
                                $value = new ObjectIterator($this, $value, $aliases[$name]);
0 ignored issues
show
Documentation introduced by
$value is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
                                break;
108
                            }
109
                            $value = $this->assignArrayToObject(
110
                                $value,
111
                                new $aliases[$name]['namespace'](),
112
                                $aliases[$name]['aliases']
113
                            );
114
                        }
115
                        break;
116
                    default:
117
                        // Do nothing here. Default cas is required by our code style standard.
118
                        break;
119
                }
120
            }
121
122 View Code Duplication
            if ($aliases[$name]['propertyType'] == 'private') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
                $object->{$aliases[$name]['methods']['setter']}($value);
124
            } else {
125
                $object->{$aliases[$name]['propertyName']} = $value;
126
            }
127
        }
128
129
        return $object;
130
    }
131
132
    /**
133
     * Converts object to an array.
134
     *
135
     * @param mixed $object
136
     * @param array $aliases
137
     *
138
     * @return array
139
     */
140
    public function convertToArray($object, $aliases = [])
141
    {
142
        if (empty($aliases)) {
143
            $aliases = $this->getAlias($object);
144
        }
145
146
        $array = [];
147
148
        // Variable $name defined in client.
149
        foreach ($aliases as $name => $alias) {
150 View Code Duplication
            if ($aliases[$name]['propertyType'] == 'private') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
                $value = $object->{$aliases[$name]['methods']['getter']}();
152
            } else {
153
                $value = $object->{$aliases[$name]['propertyName']};
154
            }
155
156
            if (isset($value)) {
157
                if (array_key_exists('aliases', $alias)) {
158
                    $new = [];
159
                    if ($alias['multiple']) {
160
                        $this->isTraversable($value);
161
                        foreach ($value as $item) {
162
                            $this->checkVariableType($item, [$alias['namespace']]);
163
                            $new[] = $this->convertToArray($item, $alias['aliases']);
164
                        }
165
                    } else {
166
                        $this->checkVariableType($value, [$alias['namespace']]);
167
                        $new = $this->convertToArray($value, $alias['aliases']);
168
                    }
169
                    $value = $new;
170
                }
171
172
                if ($value instanceof \DateTime) {
173
                    $value = $value->format(isset($alias['format']) ? $alias['format'] : \DateTime::ISO8601);
174
                }
175
176
                $array[$name] = $value;
177
            }
178
        }
179
180
        return $array;
181
    }
182
183
    /**
184
     * Check if class matches the expected one.
185
     *
186
     * @param object $object
187
     * @param array  $expectedClasses
188
     *
189
     * @throws \InvalidArgumentException
190
     */
191
    private function checkVariableType($object, array $expectedClasses)
192
    {
193
        if (!is_object($object)) {
194
            $msg = 'Expected variable of type object, got ' . gettype($object) . ". (field isn't multiple)";
195
            throw new \InvalidArgumentException($msg);
196
        }
197
198
        $class = get_class($object);
199
        if (!in_array($class, $expectedClasses)) {
200
            throw new \InvalidArgumentException("Expected object of type {$expectedClasses[0]}, got {$class}.");
201
        }
202
    }
203
204
    /**
205
     * Check if object is traversable, throw exception otherwise.
206
     *
207
     * @param mixed $value
208
     *
209
     * @return bool
210
     *
211
     * @throws \InvalidArgumentException
212
     */
213
    private function isTraversable($value)
214
    {
215
        if (!(is_array($value) || (is_object($value) && $value instanceof \Traversable))) {
216
            throw new \InvalidArgumentException("Variable isn't traversable, although field is set to multiple.");
217
        }
218
219
        return true;
220
    }
221
222
    /**
223
     * Returns aliases for certain document.
224
     *
225
     * @param DocumentInterface $document
226
     *
227
     * @return array
228
     *
229
     * @throws \DomainException
230
     */
231
    private function getAlias($document)
232
    {
233
        $class = get_class($document);
234
        $documentMapping = $this->metadataCollector->getDocumentMapping($document);
235
        if (is_array($documentMapping) && isset($documentMapping['aliases'])) {
236
            return $documentMapping['aliases'];
237
        }
238
239
        throw new \DomainException("Aliases could not be found for {$class} document.");
240
    }
241
}
242