Completed
Push — master ( 80d3da...0d64d6 )
by Karel
14s
created

AbstractElasticaToModelTransformer::transform()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 7.0145

Importance

Changes 0
Metric Value
dl 0
loc 46
ccs 28
cts 30
cp 0.9333
rs 8.2448
c 0
b 0
f 0
cc 7
nc 8
nop 1
crap 7.0145
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\Doctrine;
13
14
use Doctrine\Persistence\ManagerRegistry;
15
use FOS\ElasticaBundle\HybridResult;
16
use FOS\ElasticaBundle\Transformer\AbstractElasticaToModelTransformer as BaseTransformer;
17
use FOS\ElasticaBundle\Transformer\HighlightableModelInterface;
18
19
/**
20
 * Maps Elastica documents with Doctrine objects
21
 * This mapper assumes an exact match between
22
 * elastica documents ids and doctrine object ids.
23
 */
24
abstract class AbstractElasticaToModelTransformer extends BaseTransformer
25
{
26
    /**
27
     * Manager registry.
28
     *
29
     * @var ManagerRegistry
30
     */
31
    protected $registry = null;
32
33
    /**
34
     * Class of the model to map to the elastica documents.
35
     *
36
     * @var string
37
     */
38
    protected $objectClass = null;
39
40
    /**
41
     * Optional parameters.
42
     *
43
     * @var array
44
     */
45
    protected $options = [
46
        'hints' => [],
47
        'hydrate' => true,
48
        'identifier' => 'id',
49
        'ignore_missing' => false,
50
        'query_builder_method' => 'createQueryBuilder',
51
    ];
52
53
    /**
54
     * Instantiates a new Mapper.
55
     */
56 14
    public function __construct(ManagerRegistry $registry, string $objectClass, array $options = [])
57
    {
58 14
        $this->registry = $registry;
59 14
        $this->objectClass = $objectClass;
60 14
        $this->options = \array_merge($this->options, $options);
61 14
    }
62
63
    /**
64
     * Returns the object class that is used for conversion.
65
     */
66 1
    public function getObjectClass(): string
67
    {
68 1
        return $this->objectClass;
69
    }
70
71
    /**
72
     * Transforms an array of elastica objects into an array of
73
     * model objects fetched from the doctrine repository.
74
     *
75
     * @param array $elasticaObjects of elastica objects
76
     *
77
     * @throws \RuntimeException
78
     *
79
     * @return array
80
     **/
81 7
    public function transform(array $elasticaObjects)
82
    {
83 7
        $ids = $highlights = [];
84 7
        foreach ($elasticaObjects as $elasticaObject) {
85 7
            $ids[] = $elasticaObject->getId();
86 7
            $highlights[$elasticaObject->getId()] = $elasticaObject->getHighlights();
87
        }
88
89 7
        $objects = $this->findByIdentifiers($ids, $this->options['hydrate']);
90 7
        $objectsCnt = \count($objects);
91 7
        $elasticaObjectsCnt = \count($elasticaObjects);
92 7
        $propertyAccessor = $this->propertyAccessor;
93 7
        $identifier = $this->options['identifier'];
94 7
        if (!$this->options['ignore_missing'] && $objectsCnt < $elasticaObjectsCnt) {
95 1
            $missingIds = \array_diff($ids, \array_map(function ($object) use ($propertyAccessor, $identifier) {
96
                return $propertyAccessor->getValue($object, $identifier);
97 1
            }, $objects));
98
99 1
            throw new \RuntimeException(\sprintf('Cannot find corresponding Doctrine objects (%d) for all Elastica results (%d). Missing IDs: %s. IDs: %s', $objectsCnt, $elasticaObjectsCnt, \implode(', ', $missingIds), \implode(', ', $ids)));
100
        }
101
102 6
        foreach ($objects as $object) {
103 5
            if ($object instanceof HighlightableModelInterface) {
104 4
                $id = $propertyAccessor->getValue($object, $identifier);
105 4
                $object->setElasticHighlights($highlights[(string) $id]);
106
            }
107
        }
108
109
        // sort objects in the order of ids
110 6
        $idPos = \array_flip($ids);
111 6
        \usort(
112 6
            $objects,
113 6
            function ($a, $b) use ($idPos, $identifier, $propertyAccessor) {
114 5
                if ($this->options['hydrate']) {
115 5
                    return $idPos[(string) $propertyAccessor->getValue(
116 5
                        $a,
117
                        $identifier
118 5
                    )] > $idPos[(string) $propertyAccessor->getValue($b, $identifier)];
119
                }
120
121
                return $idPos[$a[$identifier]] > $idPos[$b[$identifier]];
122 6
            }
123
        );
124
125 6
        return $objects;
126
    }
127
128 2
    public function hybridTransform(array $elasticaObjects)
129
    {
130 2
        $indexedElasticaResults = [];
131 2
        foreach ($elasticaObjects as $elasticaObject) {
132 2
            $indexedElasticaResults[(string) $elasticaObject->getId()] = $elasticaObject;
133
        }
134
135 2
        $objects = $this->transform($elasticaObjects);
136
137 2
        $result = [];
138 2
        foreach ($objects as $object) {
139 2
            if ($this->options['hydrate']) {
140 2
                $id = $this->propertyAccessor->getValue($object, $this->options['identifier']);
141
            } else {
142
                $id = $object[$this->options['identifier']];
143
            }
144 2
            $result[] = new HybridResult($indexedElasticaResults[(string) $id], $object);
145
        }
146
147 2
        return $result;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 1
    public function getIdentifierField(): string
154
    {
155 1
        return $this->options['identifier'];
156
    }
157
158
    /**
159
     * Fetches objects by theses identifier values.
160
     *
161
     * @param array $identifierValues ids values
162
     * @param bool  $hydrate          whether or not to hydrate the objects, false returns arrays
163
     *
164
     * @return array of objects or arrays
165
     */
166
    abstract protected function findByIdentifiers(array $identifierValues, bool $hydrate);
167
}
168