Passed
Push — hans/bufferadd ( 3149c2...a9e093 )
by Simon
06:08 queued 01:33
created

DocumentFactory   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Test Coverage

Coverage 98.48%

Importance

Changes 25
Bugs 3 Features 0
Metric Value
eloc 60
dl 0
loc 219
ccs 65
cts 66
cp 0.9848
rs 10
c 25
b 3
f 0
wmc 25

11 Methods

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