ModelToElasticaAutoTransformer::normalizeValue()   B
last analyzed

Complexity

Conditions 8
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 12
cts 12
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 3
nop 1
crap 8
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <https://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\PostTransformEvent;
16
use FOS\ElasticaBundle\Event\PreTransformEvent;
17
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
18
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
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 38
    public function __construct(array $options = [], ?EventDispatcherInterface $dispatcher = null)
53
    {
54 38
        $this->options = \array_merge($this->options, $options);
55 38
        $this->dispatcher = $dispatcher;
56 38
    }
57
58
    /**
59
     * Set the PropertyAccessor.
60
     */
61 38
    public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
62
    {
63 38
        $this->propertyAccessor = $propertyAccessor;
64 38
    }
65
66
    /**
67
     * Transforms an object into an elastica object having the required keys.
68
     **/
69 26
    public function transform(object $object, array $fields): Document
70
    {
71 26
        $identifier = $this->propertyAccessor->getValue($object, $this->options['identifier']);
72
73 26
        return $this->transformObjectToDocument($object, $fields, (string) $identifier);
74
    }
75
76
    /**
77
     * transform a nested document or an object property into an array of ElasticaDocument.
78
     *
79
     * @param array|\Traversable|\ArrayAccess $objects the object to convert
80
     * @param array                           $fields  the keys we want to have in the returned array
81
     *
82
     * @return array
83
     */
84 6
    protected function transformNested($objects, array $fields)
85
    {
86 6
        if (\is_array($objects) || $objects instanceof \Traversable || $objects instanceof \ArrayAccess) {
87 3
            $documents = [];
88 3
            foreach ($objects as $object) {
89 3
                $document = $this->transformObjectToDocument($object, $fields);
90 3
                $documents[] = $document->getData();
91
            }
92
93 3
            return $documents;
94 3
        } elseif (null !== $objects) {
95 2
            $document = $this->transformObjectToDocument($objects, $fields);
96
97 2
            return $document->getData();
98
        }
99
100 1
        return [];
101
    }
102
103
    /**
104
     * Attempts to convert any type to a string or an array of strings.
105
     *
106
     * @param mixed $value
107
     *
108
     * @return string|array
109
     */
110 20
    protected function normalizeValue($value)
111
    {
112 20
        $normalizeValue = function (&$v) {
113 20
            if ($v instanceof \DateTimeInterface) {
114 2
                $v = $v->format('c');
115 20
            } elseif (!\is_scalar($v) && null !== $v) {
116 1
                $v = (string) $v;
117
            }
118 20
        };
119
120 20
        if (\is_array($value) || $value instanceof \Traversable || $value instanceof \ArrayAccess) {
121 6
            $value = \is_array($value) ? $value : \iterator_to_array($value, false);
122 6
            \array_walk_recursive($value, $normalizeValue);
123
        } else {
124 16
            $normalizeValue($value);
125
        }
126
127 20
        return $value;
128
    }
129
130
    /**
131
     * Transforms the given object to an elastica document.
132
     */
133 26
    protected function transformObjectToDocument(object $object, array $fields, string $identifier = ''): Document
134
    {
135 26
        $document = new Document($identifier, [], $this->options['index']);
136
137 26
        if ($this->dispatcher) {
138 3
            $this->dispatcher->dispatch($event = new PreTransformEvent($document, $fields, $object));
0 ignored issues
show
Documentation introduced by
$event = new \FOS\Elasti...ment, $fields, $object) is of type object<FOS\ElasticaBundl...vent\PreTransformEvent>, but the function expects a object<Symfony\Contracts\EventDispatcher\object>.

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...
139
140 3
            $document = $event->getDocument();
141
        }
142
143 26
        foreach ($fields as $key => $mapping) {
144 24
            $path = $mapping['property_path'] ?? $key;
145 24
            if (false === $path) {
146 2
                continue;
147
            }
148 24
            $value = $this->propertyAccessor->getValue($object, $path);
149
150 23
            if (isset($mapping['type'])
151 23
                && \in_array($mapping['type'], ['nested', 'object'], true)
152 23
                && isset($mapping['properties']) && !empty($mapping['properties'])
153
            ) {
154
                /* $value is a nested document or object. Transform $value into
155
                 * an array of documents, respective the mapped properties.
156
                 */
157 6
                $document->set($key, $this->transformNested($value, $mapping['properties']));
158
159 6
                continue;
160
            }
161
162 22
            if (isset($mapping['type']) && 'attachment' == $mapping['type']) {
163
                // $value is an attachment. Add it to the document.
164 2
                if ($value instanceof \SplFileInfo) {
165 1
                    $document->addFile($key, $value->getPathName());
166
                } else {
167 1
                    $document->addFileContent($key, $value);
168
                }
169
170 2
                continue;
171
            }
172
173 20
            $document->set($key, $this->normalizeValue($value));
174
        }
175
176 25
        if ($this->dispatcher) {
177 3
            $this->dispatcher->dispatch($event = new PostTransformEvent($document, $fields, $object));
0 ignored issues
show
Documentation introduced by
$event = new \FOS\Elasti...ment, $fields, $object) is of type object<FOS\ElasticaBundl...ent\PostTransformEvent>, but the function expects a object<Symfony\Contracts\EventDispatcher\object>.

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...
178
179 3
            $document = $event->getDocument();
180
        }
181
182 25
        return $document;
183
    }
184
}
185