Completed
Pull Request — master (#1569)
by
unknown
29:54 queued 20:53
created

ModelToElasticaAutoTransformer::transform()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.9666
c 0
b 0
f 0
cc 3
nc 2
nop 2
crap 3
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
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 FOS\ElasticaBundle\Transformer;
13
14
use Elastica\Document;
15
use FOS\ElasticaBundle\Event\TransformEvent;
16
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
17
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
18
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
19
20
/**
21
 * Maps Elastica documents with Doctrine objects
22
 * This mapper assumes an exact match between
23
 * elastica documents ids and doctrine object ids.
24
 */
25
class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterface
26
{
27
    /**
28
     * @var EventDispatcherInterface
29
     */
30
    protected $dispatcher;
31
32
    /**
33
     * Optional parameters.
34
     *
35
     * @var array
36
     */
37
    protected $options = [
38
        'identifier' => 'id',
39
        'index' => '',
40
    ];
41
42
    /**
43
     * PropertyAccessor instance.
44
     *
45
     * @var PropertyAccessorInterface
46
     */
47
    protected $propertyAccessor;
48
49
    /**
50
     * Instanciates a new Mapper.
51
     *
52
     * @param array                    $options
53
     * @param EventDispatcherInterface $dispatcher
54
     */
55 42 View Code Duplication
    public function __construct(array $options = [], EventDispatcherInterface $dispatcher = null)
56
    {
57 42
        $this->options = array_merge($this->options, $options);
58 42
        $this->dispatcher = $dispatcher;
59
60 42
        if (class_exists(LegacyEventDispatcherProxy::class)) {
61 42
            $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
62
        }
63 42
    }
64
65
    /**
66
     * Set the PropertyAccessor.
67
     *
68
     * @param PropertyAccessorInterface $propertyAccessor
69
     */
70 42
    public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
71
    {
72 42
        $this->propertyAccessor = $propertyAccessor;
73 42
    }
74
75
    /**
76
     * Transforms an object into an elastica object having the required keys.
77
     *
78
     * @param object $object the object to convert
79
     * @param array  $fields the keys we want to have in the returned array
80
     *
81
     * @return Document
82
     **/
83 24
    public function transform($object, array $fields)
84
    {
85 24
        $identifier = $this->propertyAccessor->getValue($object, $this->options['identifier']);
86 24
        if ($identifier && !is_scalar($identifier)) {
87 1
            $identifier = (string) $identifier;
88
        }
89
90 24
        return $this->transformObjectToDocument($object, $fields, $identifier);
91
    }
92
93
    /**
94
     * transform a nested document or an object property into an array of ElasticaDocument.
95
     *
96
     * @param array|\Traversable|\ArrayAccess $objects the object to convert
97
     * @param array                           $fields  the keys we want to have in the returned array
98
     *
99
     * @return array
100
     */
101 6
    protected function transformNested($objects, array $fields)
102
    {
103 6
        if (is_array($objects) || $objects instanceof \Traversable || $objects instanceof \ArrayAccess) {
104 3
            $documents = [];
105 3
            foreach ($objects as $object) {
106 3
                $document = $this->transformObjectToDocument($object, $fields);
107 3
                $documents[] = $document->getData();
108
            }
109
110 3
            return $documents;
111 3
        } elseif (null !== $objects) {
112 2
            $document = $this->transformObjectToDocument($objects, $fields);
113
114 2
            return $document->getData();
115
        }
116
117 1
        return [];
118
    }
119
120
    /**
121
     * Attempts to convert any type to a string or an array of strings.
122
     *
123
     * @param mixed $value
124
     *
125
     * @return string|array
126
     */
127
    protected function normalizeValue($value)
128
    {
129 14
        $normalizeValue = function (&$v) {
130 14
            if ($v instanceof \DateTimeInterface) {
131 2
                $v = $v->format('c');
132 14
            } elseif (!is_scalar($v) && !is_null($v)) {
133 1
                $v = (string) $v;
134
            }
135 14
        };
136
137 14
        if (is_array($value) || $value instanceof \Traversable || $value instanceof \ArrayAccess) {
138 5
            $value = is_array($value) ? $value : iterator_to_array($value, false);
139 5
            array_walk_recursive($value, $normalizeValue);
140
        } else {
141 10
            $normalizeValue($value);
142
        }
143
144 14
        return $value;
145
    }
146
147
    /**
148
     * Transforms the given object to an elastica document.
149
     *
150
     * @param object $object     the object to convert
151
     * @param array  $fields     the keys we want to have in the returned array
152
     * @param string $identifier the identifier for the new document
153
     *
154
     * @return Document
155
     */
156 24
    protected function transformObjectToDocument($object, array $fields, $identifier = '')
157
    {
158 24
        $document = new Document($identifier, [], '', $this->options['index']);
0 ignored issues
show
Unused Code introduced by
The call to Document::__construct() has too many arguments starting with $this->options['index'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
159
160 24 View Code Duplication
        if ($this->dispatcher) {
161 1
            $event = new TransformEvent($document, $fields, $object);
162 1
            $this->dispatcher->dispatch(TransformEvent::PRE_TRANSFORM, $event);
163
164 1
            $document = $event->getDocument();
165
        }
166
167 24
        foreach ($fields as $key => $mapping) {
168 22
            if ('_parent' == $key) {
169 4
                $property = (null !== $mapping['property']) ? $mapping['property'] : $mapping['type'];
170 4
                $value = $this->propertyAccessor->getValue($object, $property);
171 4
                $document->setParent($this->propertyAccessor->getValue($value, $mapping['identifier']));
0 ignored issues
show
Bug introduced by
The method setParent() does not seem to exist on object<Elastica\Document>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
172
173
                continue;
174
            }
175
176 18
            $path = isset($mapping['property_path']) ?
177 1
                $mapping['property_path'] :
178 18
                $key;
179 18
            if (false === $path) {
180 1
                continue;
181
            }
182 18
            $value = $this->propertyAccessor->getValue($object, $path);
183
184 17
            if (isset($mapping['type']) && in_array(
185 17
                    $mapping['type'], ['nested', 'object']
186 17
                ) && isset($mapping['properties']) && !empty($mapping['properties'])
187
            ) {
188
                /* $value is a nested document or object. Transform $value into
189
                 * an array of documents, respective the mapped properties.
190
                 */
191 6
                $document->set($key, $this->transformNested($value, $mapping['properties']));
192
193 6
                continue;
194
            }
195
196 16
            if (isset($mapping['type']) && 'attachment' == $mapping['type']) {
197
                // $value is an attachment. Add it to the document.
198 2
                if ($value instanceof \SplFileInfo) {
199 1
                    $document->addFile($key, $value->getPathName());
200
                } else {
201 1
                    $document->addFileContent($key, $value);
202
                }
203
204 2
                continue;
205
            }
206
207 14
            $document->set($key, $this->normalizeValue($value));
208
        }
209
210 19 View Code Duplication
        if ($this->dispatcher) {
211 1
            $event = new TransformEvent($document, $fields, $object);
212 1
            $this->dispatcher->dispatch(TransformEvent::POST_TRANSFORM, $event);
213
214 1
            $document = $event->getDocument();
215
        }
216
217 19
        return $document;
218
    }
219
}
220