Passed
Push — hans/Index-all-fluent-options ( 42a725...2f1f10 )
by Simon
07:16
created

DocumentFactory::buildItems()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4.0023

Importance

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