Passed
Push — hans/code-cleanup ( 74c548...547d18 )
by Simon
09:31 queued 07:32
created

BaseIndexTrait::addFulltextField()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.0187

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 10
c 2
b 1
f 0
dl 0
loc 21
ccs 10
cts 11
cp 0.9091
rs 9.6111
cc 5
nc 16
nop 3
crap 5.0187
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Traits;
5
6
use Firesphere\SolrSearch\Indexes\BaseIndex;
7
use ReflectionClass;
8
use ReflectionException;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Dev\Deprecation;
12
use SilverStripe\ORM\DataObject;
13
use SilverStripe\ORM\FieldType\DBDate;
14
use SilverStripe\ORM\FieldType\DBString;
15
use Solarium\Core\Client\Client;
16
17
/**
18
 * This is slightly cheating, but it works and also makes things more readable.
19
 *
20
 * This is slightly cheating, but it works and also makes things more readable.
21
 *
22
 * @package Firesphere\SolrSearch\Traits
23
 */
24
trait BaseIndexTrait
25
{
26
    /**
27
     * @var Client Query client
28
     */
29
    protected $client;
30
    /**
31
     * @var array Facet fields
32
     */
33
    protected $facetFields = [];
34
    /**
35
     * @var array Fulltext fields
36
     */
37
    protected $fulltextFields = [];
38
    /**
39
     * @var array Filterable fields
40
     */
41
    protected $filterFields = [];
42
    /**
43
     * @var array Sortable fields
44
     */
45
    protected $sortFields = [];
46
    /**
47
     * @var string Default search field
48
     */
49
    protected $defaultField = '_text';
50
    /**
51
     * @var array Stored fields
52
     */
53
    protected $storedFields = [];
54
    /**
55
     * @var array Fields to copy to the default fields
56
     */
57
    protected $copyFields = [
58
        '_text' => [
59
            '*',
60
        ],
61
    ];
62
    /**
63
     * usedAllFields is used to determine if the addAllFields method has been called
64
     * This is to prevent a notice if there is no yml.
65
     *
66
     * @var bool
67
     */
68
    protected $usedAllFields = false;
69
70
    /**
71
     * Return the copy fields
72
     *
73
     * @return array
74
     */
75 34
    public function getCopyFields(): array
76
    {
77 34
        return $this->copyFields;
78
    }
79
80
    /**
81
     * Set the copy fields
82
     *
83
     * @param array $copyField
84
     * @return $this
85
     */
86 74
    public function setCopyFields($copyField): self
87
    {
88 74
        $this->copyFields = $copyField;
89
90 74
        return $this;
91
    }
92
93
    /**
94
     * Return the default field for this index
95
     *
96
     * @return string
97
     */
98 35
    public function getDefaultField(): string
99
    {
100 35
        return $this->defaultField;
101
    }
102
103
    /**
104
     * Set the default field for this index
105
     *
106
     * @param string $defaultField
107
     * @return $this
108
     */
109 74
    public function setDefaultField($defaultField): self
110
    {
111 74
        $this->defaultField = $defaultField;
112
113 74
        return $this;
114
    }
115
116
    /**
117
     * Add a field to sort on
118
     *
119
     * @param $sortField
120
     * @return $this
121
     */
122 75
    public function addSortField($sortField): self
123
    {
124 75
        if (!in_array($sortField, $this->getFulltextFields(), true) &&
125 75
            !in_array($sortField, $this->getFilterFields(), true)
126
        ) {
127 1
            $this->addFulltextField($sortField);
128 1
            $this->sortFields[] = $sortField;
129
        }
130
131 75
        $this->setSortFields(array_unique($this->getSortFields()));
132
133 75
        return $this;
134
    }
135
136
    /**
137
     * Get the fulltext fields
138
     *
139
     * @return array
140
     */
141 75
    public function getFulltextFields(): array
142
    {
143 75
        return array_values(
144 75
            array_unique(
145 75
                $this->fulltextFields
146
            )
147
        );
148
    }
149
150
    /**
151
     * Set the fulltext fields
152
     *
153
     * @param array $fulltextFields
154
     * @return $this
155
     */
156 74
    public function setFulltextFields($fulltextFields): self
157
    {
158 74
        $this->fulltextFields = $fulltextFields;
159
160 74
        return $this;
161
    }
162
163
    /**
164
     * Get the filter fields
165
     *
166
     * @return array
167
     */
168 76
    public function getFilterFields(): array
169
    {
170 76
        return $this->filterFields;
171
    }
172
173
    /**
174
     * Set the filter fields
175
     *
176
     * @param array $filterFields
177
     * @return $this
178
     */
179 74
    public function setFilterFields($filterFields): self
180
    {
181 74
        $this->filterFields = $filterFields;
182
183 74
        return $this;
184
    }
185
186
    /**
187
     * Add a single Fulltext field
188
     *
189
     * @param string $fulltextField
190
     * @param null|string $forceType
191
     * @param array $options
192
     * @return $this
193
     */
194 76
    public function addFulltextField($fulltextField, $forceType = null, $options = []): self
195
    {
196 76
        if ($forceType) {
197
            Deprecation::notice('5.0', 'ForceType should be handled through casting');
198
        }
199
200 76
        $key = array_search($fulltextField, $this->getFilterFields(), true);
201
202 76
        if (!$key) {
203 76
            $this->fulltextFields[] = $fulltextField;
204
        }
205
206 76
        if (isset($options['boost'])) {
207 1
            $this->addBoostedField($fulltextField, [], $options['boost']);
208
        }
209
210 76
        if (isset($options['stored'])) {
211 1
            $this->storedFields[] = $fulltextField;
212
        }
213
214 76
        return $this;
215
    }
216
217
    /**
218
     * Add an abstract for the add Boosted Field to keep things consistent
219
     *
220
     * @param string $field
221
     * @param array|int $options
222
     * @param null|int $boost
223
     * @return mixed
224
     */
225
    abstract public function addBoostedField($field, $options = [], $boost = null);
226
227
    /**
228
     * Get the sortable fields
229
     *
230
     * @return array
231
     */
232 75
    public function getSortFields(): array
233
    {
234 75
        return $this->sortFields;
235
    }
236
237
    /**
238
     * Set/override the sortable fields
239
     *
240
     * @param array $sortFields
241
     * @return $this
242
     */
243 75
    public function setSortFields($sortFields): self
244
    {
245 75
        $this->sortFields = $sortFields;
246
247 75
        return $this;
248
    }
249
250
    /**
251
     * Add a Fulltext Field
252
     *
253
     * @param bool $includeSubclasses Compatibility mode, not actually used
254
     * @throws ReflectionException
255
     * @deprecated Please use addAllFulltextFields(). IncludeSubClasses is not used anymore
256
     */
257 1
    public function addFulltextFields($includeSubclasses = true)
258
    {
259 1
        $this->addAllFulltextFields();
260 1
    }
261
262
    /**
263
     * Add all text-type fields to the given index
264
     *
265
     * @throws ReflectionException
266
     */
267 1
    public function addAllFulltextFields()
268
    {
269 1
        $this->addAllFieldsByType(DBString::class);
270 1
    }
271
272
    /**
273
     * Add all database-backed text fields as fulltext searchable fields.
274
     *
275
     * For every class included in the index, examines those classes and all parent looking for "DBText" database
276
     * fields (Varchar, Text, HTMLText, etc) and adds them all as fulltext searchable fields.
277
     *
278
     * Note, there is no check on boosting etc. That needs to be done manually.
279
     *
280
     * @param string $dbType
281
     * @throws ReflectionException
282
     */
283 1
    protected function addAllFieldsByType($dbType = DBString::class): void
284
    {
285 1
        $this->usedAllFields = true;
286 1
        $classes = $this->getClasses();
287 1
        foreach ($classes as $key => $class) {
288 1
            $fields = DataObject::getSchema()->databaseFields($class, true);
289
290 1
            $this->addFulltextFieldsForClass($fields, $dbType);
291
        }
292 1
    }
293
294
    /**
295
     * This trait requires classes to be set, so getClasses can be called.
296
     *
297
     * @return array
298
     */
299
    abstract public function getClasses(): array;
300
301
    /**
302
     * Add all fields of a given type to the index
303
     *
304
     * @param array $fields The fields on the DataObject
305
     * @param string $dbType Class type the reflection should extend
306
     * @throws ReflectionException
307
     */
308 1
    protected function addFulltextFieldsForClass(array $fields, $dbType = DBString::class): void
309
    {
310 1
        foreach ($fields as $field => $type) {
311 1
            $pos = strpos($type, '(');
312 1
            if ($pos !== false) {
313 1
                $type = substr($type, 0, $pos);
314
            }
315 1
            $conf = Config::inst()->get(Injector::class, $type);
316 1
            $ref = new ReflectionClass($conf['class']);
317 1
            if ($ref->isSubclassOf($dbType)) {
318 1
                $this->addFulltextField($field);
319
            }
320
        }
321 1
    }
322
323
    /**
324
     * Add all date-type fields to the given index
325
     *
326
     * @throws ReflectionException
327
     */
328 1
    public function addAllDateFields()
329
    {
330 1
        $this->addAllFieldsByType(DBDate::class);
331 1
    }
332
333
    /**
334
     * Add a facet field
335
     *
336
     * @param $field
337
     * @param array $options
338
     * @return $this
339
     */
340 75
    public function addFacetField($field, $options): self
341
    {
342 75
        $this->facetFields[$field] = $options;
343
344 75
        if (!in_array($options['Field'], $this->getFilterFields(), true)) {
345 75
            $this->addFilterField($options['Field']);
346
        }
347
348 75
        return $this;
349
    }
350
351
    /**
352
     * Add a filterable field
353
     *
354
     * @param $filterField
355
     * @return $this
356
     */
357 75
    public function addFilterField($filterField): self
358
    {
359 75
        $key = array_search($filterField, $this->getFulltextFields(), true);
360 75
        if ($key === false) {
361 75
            $this->filterFields[] = $filterField;
362
        }
363
364 75
        return $this;
365
    }
366
367
    /**
368
     * Add a copy field
369
     *
370
     * @param string $field Name of the copyfield
371
     * @param array $options Array of all fields that should be copied to this copyfield
372
     * @return $this
373
     */
374 1
    public function addCopyField($field, $options): self
375
    {
376 1
        $this->copyFields[$field] = $options;
377
378 1
        return $this;
379
    }
380
381
    /**
382
     * Add a stored/fulltext field
383
     *
384
     * @param string $field
385
     * @param null|string $forceType
386
     * @param array $extraOptions
387
     * @return BaseIndex
388
     */
389 1
    public function addStoredField($field, $forceType = null, $extraOptions = []): self
390
    {
391 1
        $options = array_merge($extraOptions, ['stored' => 'true']);
392 1
        $this->addFulltextField($field, $forceType, $options);
393
394 1
        return $this;
395
    }
396
397
    /**
398
     * Get the client
399
     *
400
     * @return Client
401
     */
402 13
    public function getClient()
403
    {
404 13
        return $this->client;
405
    }
406
407
    /**
408
     * Set/override the client
409
     *
410
     * @param Client $client
411
     * @return $this
412
     */
413 1
    public function setClient($client): self
414
    {
415 1
        $this->client = $client;
416
417 1
        return $this;
418
    }
419
420
    /**
421
     * Get the stored field list
422
     *
423
     * @return array
424
     */
425 34
    public function getStoredFields(): array
426
    {
427 34
        return $this->storedFields;
428
    }
429
430
    /**
431
     * Set/override the stored field list
432
     *
433
     * @param array $storedFields
434
     * @return BaseIndex
435
     */
436 1
    public function setStoredFields(array $storedFields): self
437
    {
438 1
        $this->storedFields = $storedFields;
439
440 1
        return $this;
441
    }
442
}
443