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