Passed
Push — hans/Index-all-fluent-options ( 21dbdd...21739c )
by Simon
07:51 queued 05:43
created

DocumentFactory::buildFields()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 4
nop 4
dl 0
loc 9
ccs 7
cts 7
cp 1
crap 4
rs 10
c 0
b 0
f 0
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 LogicException;
16
use SilverStripe\Core\ClassInfo;
17
use SilverStripe\Core\Config\Configurable;
18
use SilverStripe\Core\Extensible;
19
use SilverStripe\Core\Injector\Injector;
20
use SilverStripe\ORM\DataObject;
21
use SilverStripe\ORM\FieldType\DBDate;
22
use SilverStripe\ORM\FieldType\DBField;
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 $fields
69
     * @param BaseIndex $index
70
     * @param Query $update
71
     * @return array
72
     * @throws Exception
73
     */
74 6
    public function buildItems($fields, $index, $update): array
75
    {
76 6
        $class = $this->getClass();
77 6
        $this->getFieldResolver()->setIndex($index);
78 6
        $boostFields = $index->getBoostedFields();
79 6
        $docs = [];
80 6
        $debugString = sprintf('Adding %s to %s%s', $class, $index->getIndexName(), PHP_EOL);
81 6
        $debugString .= '[';
82 6
        foreach ($this->getItems() as $item) {
83 6
            if ($item->ShowInSearch === 0) {
84
                continue;
85
            }
86 6
            $debugString .= "$item->ID, ";
87
            /** @var Document $doc */
88 6
            $doc = $update->createDocument();
89 6
            $this->addDefaultFields($doc, $item);
90
91 6
            $this->buildFields($fields, $doc, $item, $boostFields);
92 6
            $item->destroy();
93
94 6
            $docs[] = $doc;
95
        }
96
97 6
        if ($this->debug) {
98 1
            $this->getLogger()->info(rtrim($debugString, ', ') . ']' . PHP_EOL);
99
        }
100
101 6
        return $docs;
102
    }
103
104
    /**
105
     * Add fields that should always be included
106
     *
107
     * @param Document $doc
108
     * @param DataObject|DataObjectExtension $item
109
     */
110 6
    protected function addDefaultFields(Document $doc, DataObject $item)
111
    {
112 6
        $doc->setKey(SolrCoreService::ID_FIELD, $item->ClassName . '-' . $item->ID);
113 6
        $doc->addField(SolrCoreService::CLASS_ID_FIELD, $item->ID);
114 6
        $doc->addField('ClassName', $item->ClassName);
115 6
        $doc->addField('ClassHierarchy', ClassInfo::ancestry($item));
116 6
        $doc->addField('ViewStatus', $item->getViewStatus());
117 6
    }
118
119
    /**
120
     * Create the required record for a field
121
     *
122
     * @param $fields
123
     * @param Document $doc
124
     * @param DataObject $item
125
     * @param array $boostFields
126
     * @throws Exception
127
     */
128 6
    protected function buildFields($fields, Document $doc, DataObject $item, array $boostFields): void
129
    {
130 6
        foreach ($fields as $field) {
131 6
            $fieldData = $this->getFieldResolver()->resolveField($field);
132 6
            foreach ($fieldData as $dataField => $options) {
133
                // Only one field per class, so let's take the fieldData. This will override previous additions
134 6
                $this->addField($doc, $item, $options);
135 6
                if (array_key_exists($field, $boostFields)) {
136 6
                    $doc->setFieldBoost($dataField, $boostFields[$field]);
137
                }
138
            }
139
        }
140 6
    }
141
142
    /**
143
     * Add a single field to the Solr index
144
     *
145
     * @param Document $doc
146
     * @param DataObject $object
147
     * @param          $field
148
     *
149
     * @throws LogicException
150
     */
151 6
    protected function addField($doc, $object, $field): void
152
    {
153 6
        if (!$this->classIs($object, $field['origin'])) {
154 5
            return;
155
        }
156
157 6
        $this->extend('onBeforeAddField', $field);
158
159
        try {
160 6
            $valuesForField = [DataResolver::identify($object, $field['field'])];
161 6
        } catch (Exception $e) {
162 6
            $valuesForField = [];
163
        }
164
165 6
        $typeMap = Statics::getTypeMap();
166 6
        $type = $typeMap[$field['type']] ?? $typeMap['*'];
167
168 6
        foreach ($valuesForField as $value) {
169 6
            if ($value === null) {
170 4
                continue;
171
            }
172 6
            $this->extend('onBeforeAddDoc', $field, $value);
173 6
            $this->addToDoc($doc, $field, $type, $value);
174
        }
175 6
    }
176
177
    /**
178
     * Determine if the given object is one of the given type
179
     *
180
     * @param string|DataObject $class
181
     * @param array|string $base Class or list of base classes
182
     * @return bool
183
     * @todo remove in favour of the inheritance check from PHP
184
     */
185 6
    protected function classIs($class, $base): bool
186
    {
187 6
        $base = is_array($base) ? $base : [$base];
188
189 6
        foreach ($base as $nextBase) {
190 6
            if ($this->classEquals($class, $nextBase)) {
191 6
                return true;
192
            }
193
        }
194
195 5
        return false;
196
    }
197
198
    /**
199
     * Check if a base class is an instance of the expected base group
200
     *
201
     * @param string|DataObject $class
202
     * @param string $base
203
     * @return bool
204
     */
205 6
    protected function classEquals($class, $base): bool
206
    {
207 6
        return $class === $base || ($class instanceof $base);
208
    }
209
210
    /**
211
     * Push field to a document
212
     *
213
     * @param Document $doc
214
     * @param array $field
215
     * @param string $type
216
     * @param DBField|string $value
217
     */
218 6
    protected function addToDoc($doc, $field, $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($field['name']);
226
227 6
        $doc->addField($name, $value, null, 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