SchemaFactory::getCopyFieldDefinitions()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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