Passed
Push — hans/stored-field-fix ( ce0c28...ff0ce5 )
by Simon
06:41 queued 03:55
created

SchemaFactory::getFieldDefinition()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 9
nop 3
dl 0
loc 23
ccs 16
cts 16
cp 1
crap 5
rs 9.4222
c 0
b 0
f 0
1
<?php
2
/**
3
 * class SchemaFactory|Firesphere\SolrSearch\Services\SchemaFactory Base service for generating a schema
4
 *
5
 * @package Firesphere\SolrSearch\Factories
6
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
7
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
8
 */
9
10
namespace Firesphere\SolrSearch\Factories;
11
12
use Exception;
13
use Firesphere\SolrSearch\Helpers\FieldResolver;
14
use Firesphere\SolrSearch\Helpers\Statics;
15
use Firesphere\SolrSearch\Services\SolrCoreService;
16
use Firesphere\SolrSearch\Traits\GetSetSchemaFactoryTrait;
17
use SilverStripe\Control\Director;
18
use SilverStripe\Core\Injector\Injector;
19
use SilverStripe\Core\Manifest\ModuleLoader;
20
use SilverStripe\ORM\ArrayList;
21
use SilverStripe\ORM\FieldType\DBHTMLText;
22
use SilverStripe\View\ViewableData;
23
24
/**
25
 * Class SchemaFactory
26
 *
27
 * @package Firesphere\SolrSearch\Factories
28
 */
29
class SchemaFactory extends ViewableData
30
{
31
    use GetSetSchemaFactoryTrait;
32
33
    /**
34
     * @var FieldResolver The field resolver to find a field for a class
35
     */
36
    protected $fieldResolver;
37
    /**
38
     * @var SolrCoreService CoreService to use
39
     */
40
    protected $coreService;
41
    /**
42
     * @var array Base paths to the template
43
     */
44
    protected $baseTemplatePath;
45
46
    /**
47
     * SchemaFactory constructor.
48
     */
49 52
    public function __construct()
50
    {
51 52
        parent::__construct();
52 52
        $this->fieldResolver = Injector::inst()->get(FieldResolver::class);
53 52
        $this->coreService = Injector::inst()->get(SolrCoreService::class);
54 52
    }
55
56
    /**
57
     * Get all fulltext field definitions that are loaded
58
     *
59
     * @return ArrayList
60
     * @throws Exception
61
     */
62 35
    public function getFulltextFieldDefinitions()
63
    {
64 35
        $return = ArrayList::create();
65 35
        $store = $this->store;
66 35
        $this->setStore(true);
67 35
        foreach ($this->index->getFulltextFields() as $field) {
68 35
            $this->getFieldDefinition($field, $return);
69
        }
70
71 35
        $this->extend('onBeforeFulltextFields', $return);
72
73 35
        $this->setStore($store);
74
75 35
        return $return;
76
    }
77
78
    /**
79
     * Get the field definition for a single field
80
     *
81
     * @param $fieldName
82
     * @param ArrayList $return
83
     * @param null|string $copyField
84
     * @throws Exception
85
     */
86 35
    protected function getFieldDefinition($fieldName, &$return, $copyField = null)
87
    {
88 35
        $field = $this->fieldResolver->resolveField($fieldName);
89 35
        $typeMap = Statics::getTypeMap();
90 35
        $storeFields = $this->getStoreFields();
91 35
        $item = [];
92 35
        foreach ($field as $name => $options) {
93
            // Temporary short-name solution until the Introspection is properly solved
94 35
            $name = getShortFieldName($name);
95
            // Boosted fields are always stored
96 35
            $store = ($this->store || in_array($name, $storeFields) ? 'true' : 'false');
97
            $item = [
98 35
                'Field'       => $name,
99 35
                'Type'        => $typeMap[$options['type']],
100 35
                'Indexed'     => 'true',
101 35
                'Stored'      => $options['store'] ?? $store,
102 35
                'MultiValued' => $options['multi_valued'] ? 'true' : 'false',
103 35
                'Destination' => $copyField,
104
            ];
105 35
            $return->push($item);
106
        }
107
108 35
        $this->extend('onAfterFieldDefinition', $return, $item);
109 35
    }
110
111
    /**
112
     * Get the stored fields. This includes boosted and faceted fields
113
     *
114
     * @return array
115
     */
116 35
    protected function getStoreFields(): array
117
    {
118 35
        $boostedFields = $this->index->getBoostedFields();
119 35
        $storedFields = $this->index->getStoredFields();
120 35
        $facetFields = $this->index->getFacetFields();
121 35
        $facetArray = [];
122 35
        foreach ($facetFields as $key => $facetField) {
123 35
            $facetArray[] = $facetField['BaseClass'] . '.' . $facetField['Field'];
124
        }
125
126
        // Boosts, facets and obviously stored fields need to be stored
127 35
        $storeFields = array_merge($storedFields, array_keys($boostedFields), $facetArray);
128
129 35
        foreach ($storeFields as &$field) {
130 35
            $field = getShortFieldName(str_replace('.', '_', $field));
131
        }
132
133 35
        return $storeFields;
134
    }
135
136
    /**
137
     * Get the fields that should be copied
138
     *
139
     * @return ArrayList
140
     */
141 35
    public function getCopyFields()
142
    {
143 35
        $fields = $this->index->getCopyFields();
144
145 35
        $return = ArrayList::create();
146 35
        foreach ($fields as $field => $copyFields) {
147
            $item = [
148 35
                'Field' => $field,
149
            ];
150
151 35
            $return->push($item);
152
        }
153
154 35
        $this->extend('onBeforeCopyFields', $return);
155
156 35
        return $return;
157
    }
158
159
    /**
160
     * Get the definition of a copy field for determining what to load in to Solr
161
     *
162
     * @return ArrayList
163
     * @throws Exception
164
     */
165 35
    public function getCopyFieldDefinitions()
166
    {
167 35
        $copyFields = $this->index->getCopyFields();
168
169 35
        $return = ArrayList::create();
170
171 35
        foreach ($copyFields as $field => $fields) {
172
            // Allow all fields to be in a copyfield via a shorthand
173 35
            if ($fields[0] === '*') {
174 35
                $fields = $this->index->getFulltextFields();
175
            }
176
177 35
            foreach ($fields as $copyField) {
178 35
                $this->getFieldDefinition($copyField, $return, $field);
179
            }
180
        }
181
182 35
        return $return;
183
    }
184
185
    /**
186
     * Get the definitions of a filter field to load in to Solr.
187
     *
188
     * @return ArrayList
189
     * @throws Exception
190
     */
191 35
    public function getFilterFieldDefinitions()
192
    {
193 35
        $return = ArrayList::create();
194 35
        $originalStore = $this->store;
195
        // Always store every field in dev mode
196 35
        $this->setStore(Director::isDev() ? true : $this->store);
197 35
        $fields = $this->index->getFilterFields();
198 35
        foreach ($this->index->getFacetFields() as $facetField) {
199 35
            $fields[] = $facetField['Field'];
200
        }
201 35
        $fields = array_unique($fields);
202 35
        foreach ($fields as $field) {
203 35
            $this->getFieldDefinition($field, $return);
204
        }
205 35
        $this->extend('onBeforeFilterFields', $return);
206
207 35
        $this->setStore($originalStore);
208
209 35
        return $return;
210
    }
211
212
    /**
213
     * Get the types template in a rendered state
214
     *
215
     * @return DBHTMLText
216
     */
217 35
    public function getTypes()
218
    {
219 35
        $template = $this->getTemplatePathFor('schema');
220 35
        $this->setTypesTemplate($template . '/types.ss');
221
222 35
        return $this->renderWith($this->getTypesTemplate());
223
    }
224
225
    /**
226
     * Get the base path of the template given, e.g. the "schema" templates
227
     * or the "extra" templates.
228
     *
229
     * @param string $type What type of templates do we need to get
230
     * @return string
231
     */
232 36
    public function getTemplatePathFor($type): string
233
    {
234 36
        $template = $this->getBaseTemplatePath($type);
235
236
        // If the template is set, return early
237
        // Explicitly check for boolean. If it's a boolean,
238
        // the template needs to be resolved
239 36
        if (!is_bool($template)) {
240 36
            return $template;
241
        }
242 36
        $templatePath = SolrCoreService::config()->get('paths');
243 36
        $customPath = $templatePath['base_path'] ?? false;
244 36
        $path = ModuleLoader::getModule('firesphere/solr-search')->getPath();
245
246 36
        if ($customPath) {
247 1
            $path = sprintf($customPath, Director::baseFolder());
248
        }
249
250 36
        $solrVersion = $this->coreService->getSolrVersion();
251 36
        $template = sprintf($templatePath[$solrVersion][$type], $path);
252 36
        $this->setBaseTemplatePath($template, $type);
253
254 36
        return $template;
255
    }
256
257
    /**
258
     * Retrieve the base template path for a type (extra or schema)
259
     *
260
     * @param $type
261
     * @return string|bool
262
     */
263 36
    public function getBaseTemplatePath($type)
264
    {
265 36
        return $this->baseTemplatePath[$type] ?? false;
266
    }
267
268
    /**
269
     * Set the base template path for a type (extra or schema)
270
     *
271
     * @param string $baseTemplatePath
272
     * @param string $type
273
     * @return SchemaFactory
274
     */
275 36
    public function setBaseTemplatePath(string $baseTemplatePath, string $type): SchemaFactory
276
    {
277 36
        $this->baseTemplatePath[$type] = $baseTemplatePath;
278
279 36
        return $this;
280
    }
281
282
    /**
283
     * Generate the Schema xml
284
     *
285
     * @return DBHTMLText
286
     */
287 35
    public function generateSchema()
288
    {
289 35
        $template = $this->getTemplatePathFor('schema');
290 35
        $this->setTemplate($template . '/schema.ss');
291
292 35
        return $this->renderWith($this->getTemplate());
293
    }
294
295
    /**
296
     * Get any the template path for anything that needs loading in to Solr
297
     *
298
     * @return string
299
     */
300 35
    public function getExtrasPath()
301
    {
302 35
        return $this->getTemplatePathFor('extras');
303
    }
304
}
305