Passed
Push — hans/bufferadd ( d10b53...76b93a )
by Simon
06:06 queued 04:05
created

DocumentFactory   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Test Coverage

Coverage 98.59%

Importance

Changes 25
Bugs 3 Features 0
Metric Value
eloc 62
c 25
b 3
f 0
dl 0
loc 249
ccs 70
cts 71
cp 0.9859
rs 10
wmc 28

13 Methods

Rating   Name   Duplication   Size   Complexity  
A addToDoc() 0 10 3
A __construct() 0 3 1
A buildItems() 0 10 4
A buildFieldData() 0 14 3
A buildFields() 0 5 2
A classIs() 0 11 4
A setDebug() 0 5 1
A addToBuffer() 0 10 1
A addDefaultFields() 0 7 1
A addField() 0 15 3
A getValuesForField() 0 9 2
A isDebug() 0 3 1
A classEquals() 0 3 2
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Factories;
5
6
use Exception;
7
use Firesphere\SolrSearch\Extensions\DataObjectExtension;
8
use Firesphere\SolrSearch\Helpers\DataResolver;
9
use Firesphere\SolrSearch\Helpers\FieldResolver;
10
use Firesphere\SolrSearch\Helpers\Statics;
11
use Firesphere\SolrSearch\Indexes\BaseIndex;
12
use Firesphere\SolrSearch\Services\SolrCoreService;
13
use Firesphere\SolrSearch\Traits\DocumentFactoryTrait;
14
use Firesphere\SolrSearch\Traits\LoggerTrait;
15
use SilverStripe\Core\ClassInfo;
16
use SilverStripe\Core\Config\Configurable;
17
use SilverStripe\Core\Extensible;
18
use SilverStripe\Core\Injector\Injector;
19
use SilverStripe\ORM\DataObject;
20
use SilverStripe\ORM\FieldType\DBDate;
21
use SilverStripe\ORM\FieldType\DBField;
22
use Solarium\Plugin\BufferedAdd\BufferedAdd;
23
use Solarium\QueryType\Update\Query\Document;
24
use Solarium\QueryType\Update\Query\Query;
25
26
/**
27
 * Class DocumentFactory
28
 * Factory to create documents to be pushed to Solr
29
 *
30
 * @package Firesphere\SolrSearch\Factories
31
 */
32
class DocumentFactory
33
{
34
    use Configurable;
35
    use Extensible;
36
    use DocumentFactoryTrait;
37
    use LoggerTrait;
38
39
    /**
40
     * Numeral types in Solr
41
     *
42
     * @var array
43
     */
44
    protected static $numerals = [
45
        'tint',
46
        'tfloat',
47
        'tdouble',
48
    ];
49
    /**
50
     * Debug this build
51
     *
52
     * @var bool
53
     */
54
    protected $debug = false;
55
56
    /**
57
     * DocumentFactory constructor, sets up the field resolver
58
     */
59 9
    public function __construct()
60
    {
61 9
        $this->fieldResolver = Injector::inst()->get(FieldResolver::class);
62 9
    }
63
64
    /**
65
     * Note, it can only take one type of class at a time!
66
     * So make sure you properly loop and set $class
67
     *
68
     * @param array $fields
69
     * @param BaseIndex $index
70
     * @param Query $update
71
     * @param BufferedAdd $bufferAdd
72
     * @throws Exception
73
     */
74 6
    public function buildItems($fields, $index, $update, $bufferAdd): void
75
    {
76 6
        $this->getFieldResolver()->setIndex($index);
77 6
        $boostFields = $index->getBoostedFields();
78 6
        foreach ($this->getItems() as $item) {
79
            // Hard check against it being 0, if it's null, we should add the item
80 6
            if ($item->ShowInSearch === 0 || $item->ShowInSearch === false) {
81
                continue;
82
            }
83 6
            $this->addToBuffer($fields, $update, $bufferAdd, $item, $boostFields);
84
        }
85 6
    }
86
87
    /**
88
     * Add fields that should always be included
89
     *
90
     * @param Document $doc
91
     * @param DataObject|DataObjectExtension $item
92
     */
93 6
    protected function addDefaultFields(Document $doc, DataObject $item)
94
    {
95 6
        $doc->setKey(SolrCoreService::ID_FIELD, $item->ClassName . '-' . $item->ID);
96 6
        $doc->addField(SolrCoreService::CLASS_ID_FIELD, $item->ID);
97 6
        $doc->addField('ClassName', $item->ClassName);
98 6
        $doc->addField('ClassHierarchy', ClassInfo::ancestry($item));
99 6
        $doc->addField('ViewStatus', $item->getViewStatus());
100 6
    }
101
102
    /**
103
     * Create the required record for a field
104
     *
105
     * @param $fields
106
     * @param Document $doc
107
     * @param DataObject $item
108
     * @param array $boostFields
109
     * @throws Exception
110
     */
111 6
    protected function buildFields($fields, Document $doc, DataObject $item, array $boostFields): void
112
    {
113 6
        foreach ($fields as $field) {
114 6
            $fieldData = $this->getFieldResolver()->resolveField($field);
115 6
            $this->buildFieldData($doc, $item, $boostFields, $fieldData, $field);
116
        }
117 6
    }
118
119
    /**
120
     * Determine if the given object is one of the given type
121
     *
122
     * @param string|DataObject $class
123
     * @param array|string $base Class or list of base classes
124
     * @return bool
125
     * @todo remove in favour of the inheritance check from PHP
126
     */
127 6
    protected function classIs($class, $base): bool
128
    {
129 6
        $base = is_array($base) ? $base : [$base];
130
131 6
        foreach ($base as $nextBase) {
132 6
            if ($this->classEquals($class, $nextBase)) {
133 6
                return true;
134
            }
135
        }
136
137 5
        return false;
138
    }
139
140
    /**
141
     * Check if a base class is an instance of the expected base group
142
     *
143
     * @param string|DataObject $class
144
     * @param string $base
145
     * @return bool
146
     */
147 6
    protected function classEquals($class, $base): bool
148
    {
149 6
        return $class === $base || ($class instanceof $base);
150
    }
151
152
    /**
153
     * Add a single field to the Solr index
154
     *
155
     * @param Document $doc
156
     * @param DataObject $object
157
     * @param array $options
158
     */
159 6
    protected function addField($doc, $object, $options): void
160
    {
161 6
        $this->extend('onBeforeAddField', $options);
162
163 6
        $valuesForField = $this->getValuesForField($object, $options);
164
165 6
        $typeMap = Statics::getTypeMap();
166 6
        $type = $typeMap[$options['type']] ?? $typeMap['*'];
167
168 6
        foreach ($valuesForField as $value) {
169 6
            if ($value === null) {
170 4
                continue;
171
            }
172 6
            $this->extend('onBeforeAddDoc', $options, $value);
173 6
            $this->addToDoc($doc, $options, $type, $value);
174
        }
175 6
    }
176
177
    /**
178
     * Use the DataResolver to find the value(s) for a field.
179
     * Returns an array of values, and if it's multiple, it becomes a long array
180
     *
181
     * @param $object
182
     * @param $options
183
     * @return array
184
     */
185 6
    protected function getValuesForField($object, $options): array
186
    {
187
        try {
188 6
            $valuesForField = [DataResolver::identify($object, $options['fullfield'])];
189 5
        } catch (Exception $e) {
190 5
            $valuesForField = [];
191
        }
192
193 6
        return $valuesForField;
194
    }
195
196
    /**
197
     * Push field to a document
198
     *
199
     * @param Document $doc
200
     * @param array $options
201
     * @param string $type
202
     * @param DBField|string $value
203
     */
204 6
    protected function addToDoc($doc, $options, $type, $value): void
205
    {
206
        /* Solr requires dates in the form 1995-12-31T23:59:59Z, so we need to normalize to GMT */
207 6
        if ($type === 'tdate' || $value instanceof DBDate) {
208 5
            $value = gmdate('Y-m-d\TH:i:s\Z', strtotime($value));
209
        }
210
211 6
        $name = getShortFieldName($options['name']);
212
213 6
        $doc->addField($name, $value, $options['boost'], Document::MODIFIER_SET);
214 6
    }
215
216
    /**
217
     * Are we debugging?
218
     *
219
     * @return bool
220
     */
221 1
    public function isDebug(): bool
222
    {
223 1
        return $this->debug;
224
    }
225
226
    /**
227
     * Set to true if debugging should be enabled
228
     *
229
     * @param bool $debug
230
     * @return DocumentFactory
231
     */
232 7
    public function setDebug(bool $debug): DocumentFactory
233
    {
234 7
        $this->debug = $debug;
235
236 7
        return $this;
237
    }
238
239
    /**
240
     * @param Document $doc
241
     * @param DataObject $item
242
     * @param array $boostFields
243
     * @param array $fieldData
244
     * @param $field
245
     */
246 6
    protected function buildFieldData(
247
        Document $doc,
248
        DataObject $item,
249
        array $boostFields,
250
        array $fieldData,
251
        $field
252
    ): void {
253 6
        foreach ($fieldData as $dataField => $options) {
254 6
            if (!$this->classIs($item, $options['origin'])) {
255 5
                continue;
256
            }
257
258 6
            $options['boost'] = $boostFields[$field] ?? null;
259 6
            $this->addField($doc, $item, $options);
260
        }
261 6
    }
262
263
    /**
264
     * @param array $fields
265
     * @param Query $update
266
     * @param BufferedAdd $bufferAdd
267
     * @param DataObject $item
268
     * @param array $boostFields
269
     * @throws Exception
270
     */
271 6
    protected function addToBuffer($fields, $update, $bufferAdd, $item, array $boostFields): void
272
    {
273
        /** @var Document $doc */
274 6
        $doc = $update->createDocument();
275 6
        $this->addDefaultFields($doc, $item);
276
277 6
        $this->buildFields($fields, $doc, $item, $boostFields);
278 6
        $item->destroy();
279
280 6
        $bufferAdd->addDocument($doc);
281 6
    }
282
}
283