Passed
Push — sheepy/elevation-configuration ( f8fadb...77a4b0 )
by Marco
07:42
created

SchemaFactory::generateSchema()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
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 49
    public function __construct()
50
    {
51 49
        parent::__construct();
52 49
        $this->fieldResolver = Injector::inst()->get(FieldResolver::class);
53 49
        $this->coreService = Injector::inst()->get(SolrCoreService::class);
54 49
    }
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 || array_key_exists($options['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[] = $key . '.' . $facetField['Field'];
124
        }
125
126
        // Boosts, facets and obviously stored fields need to be stored
127 35
        return array_merge($storedFields, array_keys($boostedFields), $facetArray);
128
    }
129
130
    /**
131
     * Get the fields that should be copied
132
     *
133
     * @return ArrayList
134
     */
135 35
    public function getCopyFields()
136
    {
137 35
        $fields = $this->index->getCopyFields();
138
139 35
        $return = ArrayList::create();
140 35
        foreach ($fields as $field => $copyFields) {
141
            $item = [
142 35
                'Field' => $field,
143
            ];
144
145 35
            $return->push($item);
146
        }
147
148 35
        $this->extend('onBeforeCopyFields', $return);
149
150 35
        return $return;
151
    }
152
153
    /**
154
     * Get the definition of a copy field for determining what to load in to Solr
155
     *
156
     * @return ArrayList
157
     * @throws Exception
158
     */
159 35
    public function getCopyFieldDefinitions()
160
    {
161 35
        $copyFields = $this->index->getCopyFields();
162
163 35
        $return = ArrayList::create();
164
165 35
        foreach ($copyFields as $field => $fields) {
166
            // Allow all fields to be in a copyfield via a shorthand
167 35
            if ($fields[0] === '*') {
168 35
                $fields = $this->index->getFulltextFields();
169
            }
170
171 35
            foreach ($fields as $copyField) {
172 35
                $this->getFieldDefinition($copyField, $return, $field);
173
            }
174
        }
175
176 35
        return $return;
177
    }
178
179
    /**
180
     * Get the definitions of a filter field to load in to Solr.
181
     *
182
     * @return ArrayList
183
     * @throws Exception
184
     */
185 35
    public function getFilterFieldDefinitions()
186
    {
187 35
        $return = ArrayList::create();
188 35
        $originalStore = $this->store;
189
        // Always store every field in dev mode
190 35
        $this->setStore(Director::isDev() ? true : $this->store);
191 35
        $fields = $this->index->getFilterFields();
192 35
        foreach ($this->index->getFacetFields() as $facetField) {
193 35
            $fields[] = $facetField['Field'];
194
        }
195 35
        $fields = array_unique($fields);
196 35
        foreach ($fields as $field) {
197 35
            $this->getFieldDefinition($field, $return);
198
        }
199 35
        $this->extend('onBeforeFilterFields', $return);
200
201 35
        $this->setStore($originalStore);
202
203 35
        return $return;
204
    }
205
206
    /**
207
     * Get the types template in a rendered state
208
     *
209
     * @return DBHTMLText
210
     */
211 35
    public function getTypes()
212
    {
213 35
        $template = $this->getTemplatePathFor('schema');
214 35
        $this->setTypesTemplate($template . '/types.ss');
215
216 35
        return $this->renderWith($this->getTypesTemplate());
217
    }
218
219
    /**
220
     * Get the base path of the template given, e.g. the "schema" templates
221
     * or the "extra" templates.
222
     *
223
     * @param string $type What type of templates do we need to get
224
     * @return string
225
     */
226 36
    public function getTemplatePathFor($type): string
227
    {
228 36
        $template = $this->getBaseTemplatePath($type);
229
230
        // If the template is set, return early
231
        // Explicitly check for boolean. If it's a boolean,
232
        // the template needs to be resolved
233 36
        if (!is_bool($template)) {
234 36
            return $template;
235
        }
236 36
        $templatePath = SolrCoreService::config()->get('paths');
237 36
        $customPath = $templatePath['base_path'] ?? false;
238 36
        $path = ModuleLoader::getModule('firesphere/solr-search')->getPath();
239
240 36
        if ($customPath) {
241 1
            $path = sprintf($customPath, Director::baseFolder());
242
        }
243
244 36
        $solrVersion = $this->coreService->getSolrVersion();
245 36
        $template = sprintf($templatePath[$solrVersion][$type], $path);
246 36
        $this->setBaseTemplatePath($template, $type);
247
248 36
        return $template;
249
    }
250
251
    /**
252
     * Retrieve the base template path for a type (extra or schema)
253
     *
254
     * @param $type
255
     * @return string|bool
256
     */
257 36
    public function getBaseTemplatePath($type)
258
    {
259 36
        return $this->baseTemplatePath[$type] ?? false;
260
    }
261
262
    /**
263
     * Set the base template path for a type (extra or schema)
264
     *
265
     * @param string $baseTemplatePath
266
     * @param string $type
267
     * @return SchemaFactory
268
     */
269 36
    public function setBaseTemplatePath(string $baseTemplatePath, string $type): SchemaFactory
270
    {
271 36
        $this->baseTemplatePath[$type] = $baseTemplatePath;
272
273 36
        return $this;
274
    }
275
276
    /**
277
     * Generate the Schema xml
278
     *
279
     * @return DBHTMLText
280
     */
281 35
    public function generateSchema()
282
    {
283 35
        $template = $this->getTemplatePathFor('schema');
284 35
        $this->setTemplate($template . '/schema.ss');
285
286 35
        return $this->renderWith($this->getTemplate());
287
    }
288
289
    /**
290
     * Get any the template path for anything that needs loading in to Solr
291
     *
292
     * @return string
293
     */
294 35
    public function getExtrasPath()
295
    {
296 35
        return $this->getTemplatePathFor('extras');
297
    }
298
}
299