Passed
Push — bufferfix ( 8c6da4...848e8a )
by Simon
10:44
created

DocumentFactory::buildFieldData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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