Passed
Push — sheepy/elevation-configuration ( bdeab0...bcf7bc )
by Marco
18:34
created

DocumentFactory::buildField()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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