Passed
Push — hans/its-the-same ( 0a6987...73cfcc )
by Simon
04:13
created

SchemaFactory   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 89
dl 0
loc 272
ccs 97
cts 97
cp 1
rs 10
c 1
b 0
f 0
wmc 28

13 Methods

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