Passed
Push — master ( 1c87ec...276c4d )
by Timo
25:34
created

SearchUriBuilder::buildLink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2.0625
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Domain\Search\Uri;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Grouping\GroupItem;
18
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
19
use ApacheSolrForTypo3\Solr\Util;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
22
23
/**
24
 * SearchUriBuilder
25
 *
26
 * Responsibility:
27
 *
28
 * The SearchUriBuilder is responsible to build uris, that are used in the
29
 * searchContext. It can use the previous request with it's persistent
30
 * arguments to build the url for a search sub request.
31
 *
32
 * @author Frans Saris <[email protected]>
33
 * @author Timo Hund <[email protected]>
34
 * @package ApacheSolrForTypo3\Solr\Domain\Search\Uri
35
 */
36
37
class SearchUriBuilder
38
{
39
40
    /**
41
     * @var UriBuilder
42
     */
43
    protected $uriBuilder;
44
45
    /**
46
     * @var array
47
     */
48
    protected static $preCompiledLinks = [];
49
50
    /**
51
     * @var integer
52
     */
53
    protected static $hitCount;
54
55
    /**
56
     * @var integer
57
     */
58
    protected static $missCount;
59
60
    /**
61
     * @var array
62
     */
63
    protected static $additionalArgumentsCache = [];
64
65
    /**
66
     * @param UriBuilder $uriBuilder
67
     */
68 43
    public function injectUriBuilder(UriBuilder $uriBuilder)
69
    {
70 43
        $this->uriBuilder = $uriBuilder;
71 43
    }
72
73
    /**
74
     * @param SearchRequest $previousSearchRequest
75
     * @param $facetName
76
     * @param $facetValue
77
     * @return string
78
     */
79 33
    public function getAddFacetValueUri(SearchRequest $previousSearchRequest, $facetName, $facetValue)
80
    {
81
        $persistentAndFacetArguments = $previousSearchRequest
82 33
            ->getCopyForSubRequest()->removeAllGroupItemPages()->addFacetValue($facetName, $facetValue)
83 33
            ->getAsArray();
84
85 33
        $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest);
86 33
        $additionalArguments = is_array($additionalArguments) ? $additionalArguments : [];
0 ignored issues
show
introduced by
The condition is_array($additionalArguments) is always true.
Loading history...
87
88 33
        $arguments = $persistentAndFacetArguments + $additionalArguments;
89
90 33
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
91 33
        return $this->buildLink($pageUid, $arguments);
92
    }
93
94
    /**
95
     * Removes all other facet values for this name and only set's the passed value for the facet.
96
     *
97
     * @param SearchRequest $previousSearchRequest
98
     * @param $facetName
99
     * @param $facetValue
100
     * @return string
101
     */
102 1
    public function getSetFacetValueUri(SearchRequest $previousSearchRequest, $facetName, $facetValue)
103
    {
104
        $previousSearchRequest = $previousSearchRequest
105 1
            ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacetValuesByName($facetName);
106
107 1
        return $this->getAddFacetValueUri($previousSearchRequest, $facetName, $facetValue);
108
    }
109
110
    /**
111
     * @param SearchRequest $previousSearchRequest
112
     * @param $facetName
113
     * @param $facetValue
114
     * @return string
115
     */
116 5
    public function getRemoveFacetValueUri(SearchRequest $previousSearchRequest, $facetName, $facetValue)
117
    {
118
        $persistentAndFacetArguments = $previousSearchRequest
119 5
            ->getCopyForSubRequest()->removeAllGroupItemPages()->removeFacetValue($facetName, $facetValue)
120 5
            ->getAsArray();
121
122 5
        $additionalArguments = [];
123 5
        if ($previousSearchRequest->getContextTypoScriptConfiguration()->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl()) {
124 4
            $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest);
125
        }
126 5
        $arguments = $persistentAndFacetArguments + $additionalArguments;
127
128 5
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
129 5
        return $this->buildLink($pageUid, $arguments);
130
    }
131
132
    /**
133
     * @param SearchRequest $previousSearchRequest
134
     * @param $facetName
135
     * @return string
136
     */
137 1
    public function getRemoveFacetUri(SearchRequest $previousSearchRequest, $facetName)
138
    {
139
        $persistentAndFacetArguments = $previousSearchRequest
140 1
            ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacetValuesByName($facetName)
141 1
            ->getAsArray();
142
143 1
        $additionalArguments = [];
144 1
        if ($previousSearchRequest->getContextTypoScriptConfiguration()->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl()) {
145
            $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest);
146
        }
147
148 1
        $arguments = $persistentAndFacetArguments + $additionalArguments;
149
150 1
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
151 1
        return $this->buildLink($pageUid, $arguments);
152
    }
153
154
    /**
155
     * @param SearchRequest $previousSearchRequest
156
     * @return string
157
     */
158 4
    public function getRemoveAllFacetsUri(SearchRequest $previousSearchRequest)
159
    {
160
        $persistentAndFacetArguments = $previousSearchRequest
161 4
            ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacets()
162 4
            ->getAsArray();
163
164 4
        $additionalArguments = [];
165 4
        if ($previousSearchRequest->getContextTypoScriptConfiguration()->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl()) {
166 4
            $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest);
167
        }
168
169 4
        $arguments = $persistentAndFacetArguments + $additionalArguments;
170
171 4
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
172 4
        return $this->buildLink($pageUid, $arguments);
173
    }
174
175
    /**
176
     * @param SearchRequest $previousSearchRequest
177
     * @param $page
178
     * @return string
179
     */
180 11
    public function getResultPageUri(SearchRequest $previousSearchRequest, $page)
181
    {
182
        $persistentAndFacetArguments = $previousSearchRequest
183 11
            ->getCopyForSubRequest()->setPage($page)
184 11
            ->getAsArray();
185
186 11
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
187 11
        return $this->buildLink($pageUid, $persistentAndFacetArguments);
188
    }
189
190
    /**
191
     * @param SearchRequest $previousSearchRequest
192
     * @param GroupItem $groupItem
193
     * @param int $page
194
     * @return string
195
     */
196 1
    public function getResultGroupItemPageUri(SearchRequest $previousSearchRequest, GroupItem $groupItem, int $page)
197
    {
198
        $persistentAndFacetArguments = $previousSearchRequest
199 1
            ->getCopyForSubRequest()->setGroupItemPage($groupItem->getGroup()->getGroupName(), $groupItem->getGroupValue(), $page)
200 1
            ->getAsArray();
201 1
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
202 1
        return $this->buildLink($pageUid, $persistentAndFacetArguments);
203
    }
204
    /**
205
     * @param SearchRequest $previousSearchRequest
206
     * @param $queryString
207
     * @return string
208
     */
209 34
    public function getNewSearchUri(SearchRequest $previousSearchRequest, $queryString)
210
    {
211
        /** @var $request SearchRequest */
212 34
        $contextConfiguration = $previousSearchRequest->getContextTypoScriptConfiguration();
213 34
        $contextSystemLanguage = $previousSearchRequest->getContextSystemLanguageUid();
214 34
        $contextPageUid = $previousSearchRequest->getContextPageUid();
215
216 34
        $request = GeneralUtility::makeInstance(
217 34
            SearchRequest::class, [],
218 34
            /** @scrutinizer ignore-type */ $contextPageUid,
219 34
            /** @scrutinizer ignore-type */ $contextSystemLanguage,
220 34
            /** @scrutinizer ignore-type */ $contextConfiguration);
221 34
        $arguments = $request->setRawQueryString($queryString)->getAsArray();
222
223 34
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
224 34
        return $this->buildLink($pageUid, $arguments);
225
    }
226
227
    /**
228
     * @param SearchRequest $previousSearchRequest
229
     * @param $sortingName
230
     * @param $sortingDirection
231
     * @return string
232
     */
233 32
    public function getSetSortingUri(SearchRequest $previousSearchRequest, $sortingName, $sortingDirection)
234
    {
235
        $persistentAndFacetArguments = $previousSearchRequest
236 32
            ->getCopyForSubRequest()->setSorting($sortingName, $sortingDirection)
237 32
            ->getAsArray();
238
239 32
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
240 32
        return $this->buildLink($pageUid, $persistentAndFacetArguments);
241
    }
242
243
    /**
244
     * @param SearchRequest $previousSearchRequest
245
     * @return string
246
     */
247 31
    public function getRemoveSortingUri(SearchRequest $previousSearchRequest)
248
    {
249
        $persistentAndFacetArguments = $previousSearchRequest
250 31
            ->getCopyForSubRequest()->removeSorting()
251 31
            ->getAsArray();
252
253 31
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
254 31
        return $this->buildLink($pageUid, $persistentAndFacetArguments);
255
    }
256
257
    /**
258
     * @param SearchRequest $previousSearchRequest
259
     * @return string
260
     */
261 27
    public function getCurrentSearchUri(SearchRequest $previousSearchRequest)
262
    {
263
        $persistentAndFacetArguments = $previousSearchRequest
264 27
            ->getCopyForSubRequest()
265 27
            ->getAsArray();
266
267
268 27
        $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest);
269 27
        return $this->buildLink($pageUid, $persistentAndFacetArguments);
270
    }
271
272
    /**
273
     * @param SearchRequest $request
274
     * @return array
275
     */
276 34
    protected function getAdditionalArgumentsFromRequestConfiguration(SearchRequest $request)
277
    {
278 34
        if ($request->getContextTypoScriptConfiguration() == null) {
279
            return [];
280
        }
281
282 34
        $reQuestId = $request->getId();
283 34
        if (isset(self::$additionalArgumentsCache[$reQuestId])) {
284 31
            return self::$additionalArgumentsCache[$reQuestId];
285
        }
286
287 34
        self::$additionalArgumentsCache[$reQuestId] = $request->getContextTypoScriptConfiguration()
288 34
            ->getSearchFacetingFacetLinkUrlParametersAsArray();
289
290 34
        return self::$additionalArgumentsCache[$reQuestId];
291
    }
292
293
    /**
294
     * @param SearchRequest $request
295
     * @return integer|null
296
     */
297 43
    protected function getTargetPageUidFromRequestConfiguration(SearchRequest $request)
298
    {
299 43
        if ($request->getContextTypoScriptConfiguration() == null) {
300
            return null;
301
        }
302
303 43
        return $request->getContextTypoScriptConfiguration()->getSearchTargetPage();
304
    }
305
306
    /**
307
     * @param integer $pageUid
308
     * @param array $arguments
309
     * @return string
310
     */
311 43
    protected function buildLink($pageUid, array $arguments)
312
    {
313 43
        if (Util::getIsTYPO3VersionBelow9()) {
314 43
            return $this->buildLinkWithInMemoryCache($pageUid, $arguments);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...LinkWithInMemoryCache() has been deprecated: This method is deprecated and will be dropped with EXT:solr 10 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

314
            return /** @scrutinizer ignore-deprecated */ $this->buildLinkWithInMemoryCache($pageUid, $arguments);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
315
        } else {
316
            return $this->buildLinkForPageIdAndArguments($pageUid, $arguments, true);
317
        }
318
    }
319
320
    /**
321
     * Build the link with an i memory cache that reduces the amount of required typolink calls.
322
     *
323
     * @param integer $pageUid
324
     * @param array $arguments
325
     * @deprecated This method is deprecated and will be dropped with EXT:solr 10
326
     * @return string
327
     */
328 43
    protected function buildLinkWithInMemoryCache($pageUid, array $arguments)
329
    {
330 43
        $values = [];
331 43
        $structure = $arguments;
332 43
        $this->getSubstitution($structure, $values);
333 43
        $hash = md5($pageUid . json_encode($structure));
334
335 43
        if (isset(self::$preCompiledLinks[$hash])) {
336 34
            self::$hitCount++;
337 34
            $template = self::$preCompiledLinks[$hash];
338
        } else {
339 42
            self::$missCount++;
340 42
            $template = $this->buildLinkForPageIdAndArguments($pageUid, $structure, false);
341 42
            self::$preCompiledLinks[$hash] = $template;
342
        }
343
344 43
        $keys = array_map(function($value) {
345 41
            return urlencode($value);
346 43
        }, array_keys($values));
347 43
        $values = array_map(function($value) {
348 41
            return urlencode($value);
349 43
        }, $values);
350 43
        $uri = str_replace($keys, $values, $template);
351 43
        return $uri;
352
    }
353
354
    /**
355
     * @param int $pageUid
356
     * @param array $uriArguments
357
     * @param bool $useCHash
358
     * @return string
359
     */
360 42
    private function buildLinkForPageIdAndArguments($pageUid, array $uriArguments, $useCHash = false)
361
    {
362 42
        $this->uriBuilder->setTargetPageUid($pageUid);
363 42
        return $this->uriBuilder->setArguments($uriArguments)->setUseCacheHash($useCHash)->build();
364
    }
365
366
    /**
367
     * This method is used to build two arrays from a nested array. The first one represents the structure.
368
     * In this structure the values are replaced with the pass to the value. At the same time the values get collected
369
     * in the $values array, with the path as key. This can be used to build a comparable hash from the arguments
370
     * in order to reduce the amount of typolink calls
371
     *
372
     *
373
     * Example input
374
     *
375
     * $data = [
376
     *  'foo' => [
377
     *      'bar' => 111
378
     *   ]
379
     * ]
380
     *
381
     * will return:
382
     *
383
     * $structure = [
384
     *  'foo' => [
385
     *      'bar' => '###foo:bar###'
386
     *   ]
387
     * ]
388
     *
389
     * $values = [
390
     *  '###foo:bar###' => 111
391
     * ]
392
     *
393
     * @param $structure
394
     * @param $values
395
     * @param array $branch
396
     */
397 43
    protected function getSubstitution(array &$structure, array  &$values, array $branch = [])
398
    {
399 43
        foreach ($structure as $key => &$value) {
400 43
            $branch[] = $key;
401 43
            if (is_array($value)) {
402 43
                $this->getSubstitution($value, $values, $branch);
403
            } else {
404 41
                $path = '###' . implode(':', $branch) . '###';
405 41
                $values[$path] = $value;
406 43
                $structure[$key] = $path;
407
            }
408
        }
409 43
    }
410
}
411