Passed
Push — master ( 281ecc...a64e63 )
by Timo
05:09
created

AbstractIndexer::resolveFieldValue()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 73
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 9.3071

Importance

Changes 0
Metric Value
dl 0
loc 73
rs 6.6896
c 0
b 0
f 0
ccs 23
cts 36
cp 0.6389
cc 7
eloc 42
nc 10
nop 3
crap 9.3071

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2012-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\ContentObject\Classification;
28
use ApacheSolrForTypo3\Solr\ContentObject\Multivalue;
29
use ApacheSolrForTypo3\Solr\ContentObject\Relation;
30
use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
33
34
/**
35
 * An abstract indexer class to collect a few common methods shared with other
36
 * indexers.
37
 *
38
 * @author Ingo Renner <[email protected]>
39
 */
40
abstract class AbstractIndexer
41
{
42
43
    /**
44
     * Holds the type of the data to be indexed, usually that is the table name.
45
     *
46
     * @var string
47
     */
48
    protected $type = '';
49
50
    /**
51
     * Holds field names that are denied to overwrite in thy indexing configuration.
52
     *
53
     * @var array
54
     */
55
    protected static $unAllowedOverrideFields = ['type'];
56
57
    /**
58
     * @param string $solrFieldName
59
     * @return bool
60 30
     */
61
    public static function isAllowedToOverrideField($solrFieldName)
62 30
    {
63
        return !in_array($solrFieldName, static::$unAllowedOverrideFields);
64
    }
65
66
    /**
67
     * Adds fields to the document as defined in $indexingConfiguration
68
     *
69
     * @param \Apache_Solr_Document $document base document to add fields to
70
     * @param array $indexingConfiguration Indexing configuration / mapping
71
     * @param array $data Record data
72
     * @return \Apache_Solr_Document Modified document with added fields
73 19
     */
74
    protected function addDocumentFieldsFromTyposcript(
75
        \Apache_Solr_Document $document,
76
        array $indexingConfiguration,
77
        array $data
78
    ) {
79
80 19
        // mapping of record fields => solr document fields, resolving cObj
81 19
        foreach ($indexingConfiguration as $solrFieldName => $recordFieldName) {
82
            if (is_array($recordFieldName)) {
83 17
                // configuration for a content object, skipping
84
                continue;
85
            }
86 19
87
            if (!static::isAllowedToOverrideField($solrFieldName)) {
88
                throw new InvalidFieldNameException(
89
                    'Must not overwrite field .' . $solrFieldName,
90
                    1435441863
91
                );
92
            }
93 19
94 19
            $fieldValue = $this->resolveFieldValue($indexingConfiguration,
95
                $solrFieldName, $data);
96 19
97
            if (is_array($fieldValue)) {
98 9
                // multi value
99 9
                foreach ($fieldValue as $multiValue) {
100
                    $document->addField($solrFieldName, $multiValue);
101
                }
102 19
            } else {
103 19
                if ($fieldValue !== '' && $fieldValue !== null) {
104
                    $document->setField($solrFieldName, $fieldValue);
105
                }
106
            }
107
        }
108 19
109
        return $document;
110
    }
111
112
    /**
113
     * Resolves a field to its value depending on its configuration.
114
     *
115
     * This enables you to configure the indexer to put the item/record through
116
     * cObj processing if wanted/needed. Otherwise the plain item/record value
117
     * is taken.
118
     *
119
     * @param array $indexingConfiguration Indexing configuration as defined in plugin.tx_solr_index.queue.[indexingConfigurationName].fields
120
     * @param string $solrFieldName A Solr field name that is configured in the indexing configuration
121
     * @param array $data A record or item's data
122
     * @return string The resolved string value to be indexed
123 19
     */
124
    protected function resolveFieldValue(
125
        array $indexingConfiguration,
126
        $solrFieldName,
127
        array $data
128 19
    ) {
129
        $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
130 19
131
        if (isset($indexingConfiguration[$solrFieldName . '.'])) {
132
            // configuration found => need to resolve a cObj
133
134
            // need to change directory to make IMAGE content objects work in BE context
135 17
            // see http://blog.netzelf.de/lang/de/tipps-und-tricks/tslib_cobj-image-im-backend
136 17
            $backupWorkingDirectory = getcwd();
137
            chdir(PATH_site);
138 17
139 17
            $contentObject->start($data, $this->type);
140 17
            $fieldValue = $contentObject->cObjGetSingle(
141 17
                $indexingConfiguration[$solrFieldName],
142
                $indexingConfiguration[$solrFieldName . '.']
143
            );
144 17
145
            chdir($backupWorkingDirectory);
146 17
147 17
            if ($this->isSerializedValue($indexingConfiguration,
148
                $solrFieldName)
149 17
            ) {
150
                $fieldValue = unserialize($fieldValue);
151 19
            }
152 19
        } elseif (substr($indexingConfiguration[$solrFieldName], 0,
153
                1) === '<'
154
        ) {
155
            $referencedTsPath = trim(substr($indexingConfiguration[$solrFieldName],
156
                1));
157
            $typoScriptParser = GeneralUtility::makeInstance(TypoScriptParser::class);
158
            // $name and $conf is loaded with the referenced values.
159
            list($name, $conf) = $typoScriptParser->getVal($referencedTsPath,
160
                $GLOBALS['TSFE']->tmpl->setup);
161
162
            // need to change directory to make IMAGE content objects work in BE context
163
            // see http://blog.netzelf.de/lang/de/tipps-und-tricks/tslib_cobj-image-im-backend
164
            $backupWorkingDirectory = getcwd();
165
            chdir(PATH_site);
166
167
            $contentObject->start($data, $this->type);
168
            $fieldValue = $contentObject->cObjGetSingle($name, $conf);
169
170
            chdir($backupWorkingDirectory);
171
172
            if ($this->isSerializedValue($indexingConfiguration,
173
                $solrFieldName)
174
            ) {
175
                $fieldValue = unserialize($fieldValue);
176
            }
177 19
        } else {
178
            $fieldValue = $data[$indexingConfiguration[$solrFieldName]];
179
        }
180
181
        // detect and correct type for dynamic fields
182
183 19
        // find last underscore, substr from there, cut off last character (S/M)
184 19
        $fieldType = substr($solrFieldName, strrpos($solrFieldName, '_') + 1,
185 19
            -1);
186 9
        if (is_array($fieldValue)) {
187 9
            foreach ($fieldValue as $key => $value) {
188 9
                $fieldValue[$key] = $this->ensureFieldValueType($value,
189
                    $fieldType);
190
            }
191 19
        } else {
192
            $fieldValue = $this->ensureFieldValueType($fieldValue, $fieldType);
193
        }
194 19
195
        return $fieldValue;
196
    }
197
198
    // Utility methods
199
200
    /**
201
     * Uses a field's configuration to detect whether its value returned by a
202
     * content object is expected to be serialized and thus needs to be
203
     * unserialized.
204
     *
205
     * @param array $indexingConfiguration Current item's indexing configuration
206
     * @param string $solrFieldName Current field being indexed
207
     * @return bool TRUE if the value is expected to be serialized, FALSE otherwise
208 24
     */
209
    public static function isSerializedValue(array $indexingConfiguration, $solrFieldName)
210
    {
211
        $isSerialized = static::isSerializedResultFromRegisteredHook($indexingConfiguration, $solrFieldName);
212 24
        if ($isSerialized === true) {
213
            return $isSerialized;
214 24
        }
215
216
        $isSerialized = static::isSerializedResultFromCustomContentElement($indexingConfiguration, $solrFieldName);
217
        return $isSerialized;
218
    }
219
220
    /**
221
     * Checks if the response comes from a custom content element that returns a serialized value.
222
     *
223
     * @param array $indexingConfiguration
224
     * @param string $solrFieldName
225
     * @return bool
226
     */
227
    protected static function isSerializedResultFromCustomContentElement(array $indexingConfiguration, $solrFieldName): bool
228
    {
229
        $isSerialized = false;
230
231
        // SOLR_CLASSIFICATION - always returns serialized array
232
        if ($indexingConfiguration[$solrFieldName] == Classification::CONTENT_OBJECT_NAME) {
233
            $isSerialized = true;
234 24
        }
235
236
        // SOLR_MULTIVALUE - always returns serialized array
237
        if ($indexingConfiguration[$solrFieldName] == Multivalue::CONTENT_OBJECT_NAME) {
238
            $isSerialized = true;
239 24
        }
240 24
241
        // SOLR_RELATION - returns serialized array if multiValue option is set
242 11
        if ($indexingConfiguration[$solrFieldName] == Relation::CONTENT_OBJECT_NAME && !empty($indexingConfiguration[$solrFieldName . '.']['multiValue'])) {
243
            $isSerialized = true;
244
        }
245 24
246
        return $isSerialized;
247
    }
248
249
    /**
250
     * Checks registered hooks if a SerializedValueDetector detects a serialized response.
251
     *
252
     * @param array $indexingConfiguration
253
     * @param string $solrFieldName
254
     * @return bool
255 19
     */
256
    protected static function isSerializedResultFromRegisteredHook(array $indexingConfiguration, $solrFieldName)
257
    {
258 19
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['detectSerializedValue'])) {
259 19
            return false;
260
        }
261
262
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['detectSerializedValue'] as $classReference) {
263 19
            $serializedValueDetector = GeneralUtility::getUserObj($classReference);
264 19
            if (!$serializedValueDetector instanceof SerializedValueDetector) {
265
                $message = get_class($serializedValueDetector) . ' must implement interface ' . SerializedValueDetector::class;
266
                throw new \UnexpectedValueException($message, 1404471741);
267
            }
268
269
            $isSerialized = (boolean)$serializedValueDetector->isSerializedValue($indexingConfiguration, $solrFieldName);
270
            if ($isSerialized) {
271 19
                return true;
272 19
            }
273
        }
274
    }
275
276
    /**
277
     * Makes sure a field's value matches a (dynamic) field's type.
278
     *
279 19
     * @param mixed $value Value to be added to a document
280 19
     * @param string $fieldType The dynamic field's type
281 19
     * @return mixed Returns the value in the correct format for the field type
282
     */
283
    protected function ensureFieldValueType($value, $fieldType)
284
    {
285
        switch ($fieldType) {
286
            case 'int':
287
            case 'tInt':
288
                $value = intval($value);
289
                break;
290
291
            case 'float':
292 19
            case 'tFloat':
293
                $value = floatval($value);
294
                break;
295
296
            // long and double do not exist in PHP
297
            // simply make sure it somehow looks like a number
298
            // <insert PHP rant here>
299
            case 'long':
300
            case 'tLong':
301
                // remove anything that's not a number or negative/minus sign
302
                $value = preg_replace('/[^0-9\\-]/', '', $value);
303
                if (trim($value) === '') {
304
                    $value = 0;
305
                }
306
                break;
307
            case 'double':
308
            case 'tDouble':
309
            case 'tDouble4':
310
                // as long as it's numeric we'll take it, int or float doesn't matter
311
                if (!is_numeric($value)) {
312
                    $value = 0;
313
                }
314
                break;
315
316
            default:
317
                // assume things are correct for non-dynamic fields
318
        }
319
320
        return $value;
321
    }
322
}
323