Passed
Push — hans/bufferadd ( 9102bb...ad9db8 )
by Simon
06:14
created

DocumentFactory   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 254
Duplicated Lines 0 %

Test Coverage

Coverage 98.67%

Importance

Changes 26
Bugs 3 Features 0
Metric Value
eloc 67
c 26
b 3
f 0
dl 0
loc 254
ccs 74
cts 75
cp 0.9867
rs 10
wmc 28

13 Methods

Rating   Name   Duplication   Size   Complexity  
A isDebug() 0 3 1
A __construct() 0 3 1
A setDebug() 0 5 1
A addToDoc() 0 10 3
A buildItems() 0 14 4
A buildFieldData() 0 14 3
A buildFields() 0 5 2
A classIs() 0 11 4
A addToBuffer() 0 12 1
A addDefaultFields() 0 7 1
A addField() 0 15 3
A getValuesForField() 0 9 2
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
        $passes = [
79 6
            'update' => $update,
80 6
            'buffer' => $bufferAdd,
81
        ];
82 6
        foreach ($this->getItems() as $item) {
83
            // Hard check against it being 0, if it's null, we should add the item
84 6
            if ($item->ShowInSearch === 0 || $item->ShowInSearch === false) {
85
                continue;
86
            }
87 6
            $this->addToBuffer($fields, $passes, $item, $boostFields);
88
        }
89 6
    }
90
91
    /**
92
     * Add fields that should always be included
93
     *
94
     * @param Document $doc
95
     * @param DataObject|DataObjectExtension $item
96
     */
97 6
    protected function addDefaultFields(Document $doc, DataObject $item)
98
    {
99 6
        $doc->setKey(SolrCoreService::ID_FIELD, $item->ClassName . '-' . $item->ID);
100 6
        $doc->addField(SolrCoreService::CLASS_ID_FIELD, $item->ID);
101 6
        $doc->addField('ClassName', $item->ClassName);
102 6
        $doc->addField('ClassHierarchy', ClassInfo::ancestry($item));
103 6
        $doc->addField('ViewStatus', $item->getViewStatus());
104 6
    }
105
106
    /**
107
     * Create the required record for a field
108
     *
109
     * @param $fields
110
     * @param Document $doc
111
     * @param DataObject $item
112
     * @param array $boostFields
113
     * @throws Exception
114
     */
115 6
    protected function buildFields($fields, Document $doc, DataObject $item, array $boostFields): void
116
    {
117 6
        foreach ($fields as $field) {
118 6
            $fieldData = $this->getFieldResolver()->resolveField($field);
119 6
            $this->buildFieldData($doc, $item, $boostFields, $fieldData, $field);
120
        }
121 6
    }
122
123
    /**
124
     * Determine if the given object is one of the given type
125
     *
126
     * @param string|DataObject $class
127
     * @param array|string $base Class or list of base classes
128
     * @return bool
129
     * @todo remove in favour of the inheritance check from PHP
130
     */
131 6
    protected function classIs($class, $base): bool
132
    {
133 6
        $base = is_array($base) ? $base : [$base];
134
135 6
        foreach ($base as $nextBase) {
136 6
            if ($this->classEquals($class, $nextBase)) {
137 6
                return true;
138
            }
139
        }
140
141 5
        return false;
142
    }
143
144
    /**
145
     * Check if a base class is an instance of the expected base group
146
     *
147
     * @param string|DataObject $class
148
     * @param string $base
149
     * @return bool
150
     */
151 6
    protected function classEquals($class, $base): bool
152
    {
153 6
        return $class === $base || ($class instanceof $base);
154
    }
155
156
    /**
157
     * Add a single field to the Solr index
158
     *
159
     * @param Document $doc
160
     * @param DataObject $object
161
     * @param array $options
162
     */
163 6
    protected function addField($doc, $object, $options): void
164
    {
165 6
        $this->extend('onBeforeAddField', $options);
166
167 6
        $valuesForField = $this->getValuesForField($object, $options);
168
169 6
        $typeMap = Statics::getTypeMap();
170 6
        $type = $typeMap[$options['type']] ?? $typeMap['*'];
171
172 6
        foreach ($valuesForField as $value) {
173 6
            if ($value === null) {
174 4
                continue;
175
            }
176 6
            $this->extend('onBeforeAddDoc', $options, $value);
177 6
            $this->addToDoc($doc, $options, $type, $value);
178
        }
179 6
    }
180
181
    /**
182
     * Use the DataResolver to find the value(s) for a field.
183
     * Returns an array of values, and if it's multiple, it becomes a long array
184
     *
185
     * @param $object
186
     * @param $options
187
     * @return array
188
     */
189 6
    protected function getValuesForField($object, $options): array
190
    {
191
        try {
192 6
            $valuesForField = [DataResolver::identify($object, $options['fullfield'])];
193 5
        } catch (Exception $e) {
194 5
            $valuesForField = [];
195
        }
196
197 6
        return $valuesForField;
198
    }
199
200
    /**
201
     * Push field to a document
202
     *
203
     * @param Document $doc
204
     * @param array $options
205
     * @param string $type
206
     * @param DBField|string $value
207
     */
208 6
    protected function addToDoc($doc, $options, $type, $value): void
209
    {
210
        /* Solr requires dates in the form 1995-12-31T23:59:59Z, so we need to normalize to GMT */
211 6
        if ($type === 'tdate' || $value instanceof DBDate) {
212 5
            $value = gmdate('Y-m-d\TH:i:s\Z', strtotime($value));
213
        }
214
215 6
        $name = getShortFieldName($options['name']);
216
217 6
        $doc->addField($name, $value, $options['boost'], Document::MODIFIER_SET);
218 6
    }
219
220
    /**
221
     * Are we debugging?
222
     *
223
     * @return bool
224
     */
225 1
    public function isDebug(): bool
226
    {
227 1
        return $this->debug;
228
    }
229
230
    /**
231
     * Set to true if debugging should be enabled
232
     *
233
     * @param bool $debug
234
     * @return DocumentFactory
235
     */
236 7
    public function setDebug(bool $debug): DocumentFactory
237
    {
238 7
        $this->debug = $debug;
239
240 7
        return $this;
241
    }
242
243
    /**
244
     * @param Document $doc
245
     * @param DataObject $item
246
     * @param array $boostFields
247
     * @param array $fieldData
248
     * @param $field
249
     */
250 6
    protected function buildFieldData(
251
        Document $doc,
252
        DataObject $item,
253
        array $boostFields,
254
        array $fieldData,
255
        $field
256
    ): void {
257 6
        foreach ($fieldData as $dataField => $options) {
258 6
            if (!$this->classIs($item, $options['origin'])) {
259 5
                continue;
260
            }
261
262 6
            $options['boost'] = $boostFields[$field] ?? null;
263 6
            $this->addField($doc, $item, $options);
264
        }
265 6
    }
266
267
    /**
268
     * @param array $fields
269
     * @param array|[Query|BufferedAdd] $passes
0 ignored issues
show
Documentation Bug introduced by
The doc comment array|[Query|BufferedAdd] at position 2 could not be parsed: Unknown type name '[' at position 2 in array|[Query|BufferedAdd].
Loading history...
270
     * @param DataObject $item
271
     * @param array $boostFields
272
     * @throws Exception
273
     */
274 6
    protected function addToBuffer(array $fields, array $passes, DataObject $item, array $boostFields): void
275
    {
276 6
        $update = $passes['update'];
277 6
        $bufferAdd = $passes['buffer'];
278
        /** @var Document $doc */
279 6
        $doc = $update->createDocument();
280 6
        $this->addDefaultFields($doc, $item);
281
282 6
        $this->buildFields($fields, $doc, $item, $boostFields);
283 6
        $item->destroy();
284
285 6
        $bufferAdd->addDocument($doc);
286 6
    }
287
}
288