Passed
Push — hans/bufferadd ( dd4951...8221cf )
by Simon
08:32 queued 06:25
created

DocumentFactory::classEquals()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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