Completed
Pull Request — master (#993)
by David
16:22
created

setPropertyAccessor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace FOS\ElasticaBundle\Transformer;
4
5
use FOS\ElasticaBundle\Event\TransformEvent;
6
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
7
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
8
use Elastica\Document;
9
10
/**
11
 * Maps Elastica documents with Doctrine objects
12
 * This mapper assumes an exact match between
13
 * elastica documents ids and doctrine object ids.
14
 */
15
class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterface
16
{
17
    /**
18
     * @var EventDispatcherInterface
19
     */
20
    protected $dispatcher;
21
22
    /**
23
     * Optional parameters.
24
     *
25
     * @var array
26
     */
27
    protected $options = array(
28
        'identifier' => 'id',
29
    );
30
31
    /**
32
     * PropertyAccessor instance.
33
     *
34
     * @var PropertyAccessorInterface
35
     */
36
    protected $propertyAccessor;
37
38
    /**
39
     * Instanciates a new Mapper.
40
     *
41
     * @param array                    $options
42
     * @param EventDispatcherInterface $dispatcher
43
     */
44 40
    public function __construct(array $options = array(), EventDispatcherInterface $dispatcher = null)
45
    {
46 40
        $this->options = array_merge($this->options, $options);
47 40
        $this->dispatcher = $dispatcher;
48 40
    }
49
50
    /**
51
     * Set the PropertyAccessor.
52
     *
53
     * @param PropertyAccessorInterface $propertyAccessor
54
     */
55 40
    public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
56
    {
57 40
        $this->propertyAccessor = $propertyAccessor;
58 40
    }
59
60
    /**
61
     * Transforms an object into an elastica object having the required keys.
62
     *
63
     * @param object $object the object to convert
64
     * @param array  $fields the keys we want to have in the returned array
65
     *
66
     * @return Document
67
     **/
68 26
    public function transform($object, array $fields)
69
    {
70 26
        $identifier = $this->propertyAccessor->getValue($object, $this->options['identifier']);
71 26
        $document = $this->transformObjectToDocument($object, $fields, $identifier);
72
73 25
        return $document;
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 4
    protected function transformNested($objects, array $fields)
85
    {
86 4
        if (is_array($objects) || $objects instanceof \Traversable || $objects instanceof \ArrayAccess) {
87 3
            $documents = array();
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 1
        } elseif (null !== $objects) {
95 1
            $document = $this->transformObjectToDocument($objects, $fields);
96
97 1
            return $document->getData();
98
        }
99
100
        return array();
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
    protected function normalizeValue($value)
111
    {
112 18
        $normalizeValue = function (&$v) {
113 18
            if ($v instanceof \DateTime) {
114 2
                $v = $v->format('c');
115 18
            } elseif (!is_scalar($v) && !is_null($v)) {
116
                $v = (string) $v;
117
            }
118 18
        };
119
120 18
        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 14
            $normalizeValue($value);
125
        }
126
127 18
        return $value;
128
    }
129
130
    /**
131
     * Transforms the given object to an elastica document
132
     *
133
     * @param object $object the object to convert
134
     * @param array  $fields the keys we want to have in the returned array
135
     * @param string $identifier the identifier for the new document
136
     * @return Document
137
     */
138 26
    protected function transformObjectToDocument($object, array $fields, $identifier = '')
139
    {
140 26
        $document = new Document($identifier);
141
142 26
        foreach ($fields as $key => $mapping) {
143 25
            if ($key == '_parent') {
144 4
                $property = (null !== $mapping['property']) ? $mapping['property'] : $mapping['type'];
145 4
                $value = $this->propertyAccessor->getValue($object, $property);
146 4
                $document->setParent($this->propertyAccessor->getValue($value, $mapping['identifier']));
147
148 4
                continue;
149
            }
150
151 21
            $path = isset($mapping['property_path']) ?
152 2
                $mapping['property_path'] :
153 21
                $key;
154 21
            if (false === $path) {
155 2
                continue;
156
            }
157 21
            $value = $this->propertyAccessor->getValue($object, $path);
158
159 20
            if (isset($mapping['type']) && in_array(
160 20
                    $mapping['type'], array('nested', 'object')
161 20
                ) && isset($mapping['properties']) && !empty($mapping['properties'])
162
            ) {
163
                /* $value is a nested document or object. Transform $value into
164
                 * an array of documents, respective the mapped properties.
165
                 */
166 4
                $document->set($key, $this->transformNested($value, $mapping['properties']));
167
168 4
                continue;
169
            }
170
171 20
            if (isset($mapping['type']) && $mapping['type'] == 'attachment') {
172
                // $value is an attachment. Add it to the document.
173 2
                if ($value instanceof \SplFileInfo) {
174 2
                    $document->addFile($key, $value->getPathName());
175
                } else {
176
                    $document->addFileContent($key, $value);
177
                }
178
179 2
                continue;
180
            }
181
182 18
            $document->set($key, $this->normalizeValue($value));
183
        }
184
185 25
        if ($this->dispatcher) {
186 3
            $event = new TransformEvent($document, $fields, $object);
187 3
            $this->dispatcher->dispatch(TransformEvent::POST_TRANSFORM, $event);
188
189 3
            $document = $event->getDocument();
190
        }
191
192 25
        return $document;
193
    }
194
}
195