Passed
Pull Request — master (#216)
by Simon
08:31 queued 06:08
created

SchemaFactory::getExtrasPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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