Passed
Pull Request — master (#1249)
by
unknown
19:13
created

SearchRequest::removeFacetValue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.0072

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 12
cts 13
cp 0.9231
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 2
crap 4.0072
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Search;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2015-2016 Timo Schmidt <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 2 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
28
use ApacheSolrForTypo3\Solr\System\Util\ArrayAccessor;
29
use TYPO3\CMS\Core\Utility\ArrayUtility;
30
31
/**
32
 * The searchRequest is used to act as an api to the arguments that have been passed
33
 * with GET and POST.
34
 *
35
 * @author Timo Schmidt <[email protected]>
36
 */
37
class SearchRequest
38
{
39
    /**
40
     * @var string
41
     */
42
    protected $id;
43
44
    /**
45
     * @var string
46
     */
47
    protected $argumentNameSpace = 'tx_solr';
48
49
    /**
50
     * Arguments that should be kept for sub requests.
51
     *
52
     * @var array
53
     */
54
    protected $persistentArgumentsPaths = ['q', 'tx_solr:filter', 'tx_solr:sort'];
55
56
    /**
57
     * @var bool
58
     */
59
    protected $stateChanged = false;
60
61
    /**
62
     * @var ArrayAccessor
63
     */
64
    protected $argumentsAccessor;
65
66
    /**
67
     * The sys_language_uid that was used in the context where the request was build.
68
     * This could be different from the "L" parameter and and not relevant for urls,
69
     * because typolink itself will handle it.
70
     *
71
     * @var int
72
     */
73
    protected $contextSystemLanguageUid;
74
75
    /**
76
     * The page_uid that was used in the context where the request was build.
77
     *
78
     * The pageUid is not relevant for the typolink additionalArguments and therefore
79
     * a separate property.
80
     *
81
     * @var int
82
     */
83
    protected $contextPageUid;
84
85
    /**
86
     * @var TypoScriptConfiguration
87
     */
88
    protected $contextTypoScriptConfiguration;
89
90
    /**
91
     * @var array
92
     */
93
    protected $persistedArguments = [];
94
95
    /**
96
     * @param array $argumentsArray
97
     * @param int $pageUid
98
     * @param int $sysLanguageUid
99
     * @param TypoScriptConfiguration $typoScriptConfiguration
100
     */
101 57
    public function __construct(array $argumentsArray = [], $pageUid = 0, $sysLanguageUid = 0, TypoScriptConfiguration $typoScriptConfiguration = null)
102
    {
103 57
        $this->stateChanged = true;
104 57
        $this->persistedArguments = $argumentsArray;
105 57
        $this->contextPageUid = $pageUid;
106 57
        $this->contextSystemLanguageUid = $sysLanguageUid;
107 57
        $this->contextTypoScriptConfiguration = $typoScriptConfiguration;
108 57
        $this->id = spl_object_hash($this);
109 57
        $this->reset();
110 57
    }
111
112
    /**
113
     * @return string
114
     */
115 1
    public function getId()
116
    {
117 1
        return $this->id;
118
    }
119
120
    /**
121
     * Can be used do merge arguments into the request arguments
122
     *
123
     * @param array $argumentsToMerge
124
     * @return SearchRequest
125
     */
126 24
    public function mergeArguments(array $argumentsToMerge)
127
    {
128 24
        ArrayUtility::mergeRecursiveWithOverrule(
129 24
            $this->persistedArguments,
130
            $argumentsToMerge
131 24
        );
132
133 24
        $this->reset();
134
135 24
        return $this;
136
    }
137
138
    /**
139
     * Helper method to prefix an accessor with the arguments namespace.
140
     *
141
     * @param string $path
142
     * @return string
143
     */
144 48
    protected function prefixWithNamespace($path)
145
    {
146 48
        return $this->argumentNameSpace . ':' . $path;
147
    }
148
149
    /**
150
     * @return array
151
     */
152 1
    public function getActiveFacetNames()
153
    {
154 1
        $activeFacets = $this->getActiveFacets();
155 1
        $facetNames = [];
156
157
        array_map(function ($activeFacet) use (&$facetNames) {
158 1
            $facetNames[] = substr($activeFacet, 0, strpos($activeFacet, ':'));
159 1
        }, $activeFacets);
160
161 1
        return $facetNames;
162
    }
163
164
    /**
165
     * Returns all facet values for a certain facetName
166
     * @param string $facetName
167
     * @return array
168
     */
169 1
    public function getActiveFacetValuesByName($facetName)
170
    {
171 1
        $values = [];
172 1
        $activeFacets = $this->getActiveFacets();
173
174
        array_map(function ($activeFacet) use (&$values, $facetName) {
175 1
            $parts = explode(':', $activeFacet, 2);
176 1
            if ($parts[0] === $facetName) {
177 1
                $values[] = $parts[1];
178 1
            }
179 1
        }, $activeFacets);
180
181 1
        return $values;
182
    }
183
184
    /**
185
     * @return array
186
     */
187 11
    protected function getActiveFacets()
188
    {
189 11
        $path = $this->prefixWithNamespace('filter');
190 11
        $pathValue = $this->argumentsAccessor->get($path, []);
191
192 11
        return is_array($pathValue) ? $pathValue : [];
193
    }
194
195
    /**
196
     * @return int
197
     */
198 2
    public function getActiveFacetCount()
199
    {
200 2
        return count($this->getActiveFacets());
201
    }
202
203
    /**
204
     * @param $activeFacets
205
     *
206
     * @return SearchRequest
207
     */
208 8
    protected function setActiveFacets($activeFacets = [])
209
    {
210 8
        $path = $this->prefixWithNamespace('filter');
211 8
        $this->argumentsAccessor->set($path, $activeFacets);
212
213 8
        return $this;
214
    }
215
216
    /**
217
     * Adds a facet value to the request.
218
     *
219
     * @param string $facetName
220
     * @param mixed $facetValue
221
     *
222
     * @return SearchRequest
223
     */
224 6
    public function addFacetValue($facetName, $facetValue)
225
    {
226 6
        if ($this->getHasFacetValue($facetName, $facetValue)) {
227
            return $this;
228
        }
229
230 6
        $facetValues = $this->getActiveFacets();
231 6
        $facetValues[] = $facetName . ':' . $facetValue;
232 6
        $this->setActiveFacets($facetValues);
233
234 6
        $this->stateChanged = true;
235 6
        return $this;
236
    }
237
238
    /**
239
     * Removes a facet value from the request.
240
     *
241
     * @param string $facetName
242
     * @param mixed $facetValue
243
     *
244
     * @return SearchRequest
245
     */
246 1
    public function removeFacetValue($facetName, $facetValue)
247
    {
248 1
        if (!$this->getHasFacetValue($facetName, $facetValue)) {
249
            return $this;
250
        }
251 1
        $facetValues = $this->getActiveFacets();
252 1
        $facetValueToLookFor = $facetName . ':' . $facetValue;
253
254 1
        foreach ($facetValues as $index => $facetValue) {
255 1
            if ($facetValue === $facetValueToLookFor) {
256 1
                unset($facetValues[$index]);
257 1
                break;
258
            }
259 1
        }
260
261 1
        $this->setActiveFacets($facetValues);
262 1
        $this->stateChanged = true;
263 1
        return $this;
264
    }
265
266
    /**
267
     * Removes all facet values from the request by a certain facet name
268
     *
269
     * @param string $facetName
270
     *
271
     * @return SearchRequest
272
     */
273 1
    public function removeAllFacetValuesByName($facetName)
274
    {
275 1
        $facetValues = $this->getActiveFacets();
276 1
        $facetValues = array_filter($facetValues, function ($facetValue) use ($facetName) {
277 1
            $parts = explode(':', $facetValue, 2);
278 1
            return $parts[0] !== $facetName;
279 1
        });
280
281 1
        $this->setActiveFacets($facetValues);
282 1
        $this->stateChanged = true;
283 1
        return $this;
284
    }
285
286
    /**
287
     * Removes all active facets from the request.
288
     *
289
     * @return SearchRequest
290
     */
291 1
    public function removeAllFacets()
292
    {
293 1
        $path = $this->prefixWithNamespace('filter');
294 1
        $this->argumentsAccessor->reset($path);
295 1
        $this->stateChanged = true;
296 1
        return $this;
297
    }
298
299
    /**
300
     * @param string $facetName
301
     * @param mixed $facetValue
302
     * @return bool
303
     */
304 7
    public function getHasFacetValue($facetName, $facetValue)
305
    {
306 7
        $facetNameAndValueToCheck = $facetName . ':' . $facetValue;
307 7
        return in_array($facetNameAndValueToCheck, $this->getActiveFacets());
308
    }
309
310
    /**
311
     * @return bool
312
     */
313 3
    public function getHasSorting()
314
    {
315 3
        $path = $this->prefixWithNamespace('sort');
316 3
        return $this->argumentsAccessor->has($path);
317
    }
318
319
    /**
320
     * Returns the sorting string in the url e.g. title asc.
321
     *
322
     * @return string
323
     */
324 2
    protected function getSorting()
325
    {
326 2
        $path = $this->prefixWithNamespace('sort');
327 2
        return $this->argumentsAccessor->get($path);
328
    }
329
330
    /**
331
     * Helper function to get the sorting configuration name or direction.
332
     *
333
     * @param int $index
334
     * @return string
335
     */
336 2
    protected function getSortingPart($index)
337
    {
338 2
        $sorting = $this->getSorting();
339 2
        if ($sorting === null) {
340
            return null;
341
        }
342
343 2
        $parts = explode(' ', $sorting);
344 2
        return isset($parts[$index]) ? $parts[$index] : null;
345
    }
346
347
    /**
348
     * Returns the sorting configuration name that is currently used.
349
     *
350
     * @return string
351
     */
352 2
    public function getSortingName()
353
    {
354 2
        return $this->getSortingPart(0);
355
    }
356
357
    /**
358
     * Returns the sorting direction that is currently used.
359
     *
360
     * @return string
361
     */
362 1
    public function getSortingDirection()
363
    {
364 1
        return strtolower($this->getSortingPart(1));
365
    }
366
367
    /**
368
     * @return SearchRequest
369
     */
370 1
    public function removeSorting()
371
    {
372 1
        $path = $this->prefixWithNamespace('sort');
373 1
        $this->argumentsAccessor->reset($path);
374 1
        $this->stateChanged = true;
375 1
        return $this;
376
    }
377
378
    /**
379
     * @param string $sortingName
380
     * @param string $direction (asc or desc)
381
     *
382
     * @return SearchRequest
383
     */
384 1
    public function setSorting($sortingName, $direction = 'asc')
385
    {
386 1
        $value = $sortingName . ' ' . $direction;
387 1
        $path = $this->prefixWithNamespace('sort');
388 1
        $this->argumentsAccessor->set($path, $value);
389 1
        $this->stateChanged = true;
390 1
        return $this;
391
    }
392
393
    /**
394
     * Method to set the paginated page of the search
395
     *
396
     * @param int $page
397
     * @return SearchRequest
398
     */
399 1
    public function setPage($page)
400
    {
401 1
        $this->stateChanged = true;
402 1
        $path = $this->prefixWithNamespace('page');
403 1
        $this->argumentsAccessor->set($path, $page);
404 1
        return $this;
405
    }
406
407
    /**
408
     * Returns the passed page.
409
     *
410
     * @return int|null
411
     */
412 33
    public function getPage()
413
    {
414 33
        $path = $this->prefixWithNamespace('page');
415 33
        return $this->argumentsAccessor->get($path);
416
    }
417
418
    /**
419
     * Method to overwrite the query string.
420
     *
421
     * @param string $rawQueryString
422
     * @return SearchRequest
423
     */
424 10
    public function setRawQueryString($rawQueryString)
425
    {
426 10
        $this->stateChanged = true;
427 10
        $this->argumentsAccessor->set('q', $rawQueryString);
428 10
        return $this;
429
    }
430
431
    /**
432
     * Returns the passed rawQueryString.
433
     *
434
     * @return string
435
     */
436 32
    public function getRawUserQuery()
437
    {
438 32
        return $this->argumentsAccessor->get('q');
439
    }
440
441
    /**
442
     * Method to check if the query string is an empty string
443
     * (also empty string or whitespaces only are handled as empty).
444
     *
445
     * When no query string is set (null) the method returns false.
446
     * @return bool
447
     */
448 34
    public function getRawUserQueryIsEmptyString()
449
    {
450 34
        $query = $this->argumentsAccessor->get('q', null);
451
452 34
        if ($query === null) {
453 2
            return false;
454
        }
455
456 32
        if (trim($query) === '') {
457 4
            return true;
458
        }
459
460 28
        return false;
461
    }
462
463
    /**
464
     * This method returns true when no querystring is present at all.
465
     * Which means no search by the user was triggered
466
     *
467
     * @return bool
468
     */
469 34
    public function getRawUserQueryIsNull()
470
    {
471 34
        $query = $this->argumentsAccessor->get('q', null);
472 34
        return $query === null;
473
    }
474
475
    /**
476
     * Sets the results per page that are used during search.
477
     *
478
     * @param int $resultsPerPage
479
     * @return SearchRequest
480
     */
481 1
    public function setResultsPerPage($resultsPerPage)
482
    {
483 1
        $path = $this->prefixWithNamespace('resultsPerPage');
484 1
        $this->argumentsAccessor->set($path, $resultsPerPage);
485 1
        $this->stateChanged = true;
486
487 1
        return $this;
488
    }
489
490
    /**
491
     * @return bool
492
     */
493 1
    public function getStateChanged()
494
    {
495 1
        return $this->stateChanged;
496
    }
497
498
    /**
499
     * Returns the passed resultsPerPage value
500
     * @return int|null
501
     */
502 31
    public function getResultsPerPage()
503
    {
504 31
        $path = $this->prefixWithNamespace('resultsPerPage');
505 31
        return $this->argumentsAccessor->get($path);
506
    }
507
508
    /**
509
     * @return int
510
     */
511 1
    public function getContextSystemLanguageUid()
512
    {
513 1
        return $this->contextSystemLanguageUid;
514
    }
515
516
    /**
517
     * @return int
518
     */
519 2
    public function getContextPageUid()
520
    {
521 2
        return $this->contextPageUid;
522
    }
523
524
    /**
525
     * Get contextTypoScriptConfiguration
526
     *
527
     * @return TypoScriptConfiguration
528
     */
529 2
    public function getContextTypoScriptConfiguration()
530
    {
531 2
        return $this->contextTypoScriptConfiguration;
532
    }
533
534
    /**
535
     * Assigns the last known persistedArguments and restores their state.
536
     *
537
     * @return SearchRequest
538
     */
539 57
    public function reset()
540
    {
541 57
        $this->argumentsAccessor = new ArrayAccessor($this->persistedArguments);
542 57
        $this->stateChanged = false;
543 57
        return $this;
544
    }
545
546
    /**
547
     * This can be used to start a new sub request, e.g. for a faceted search.
548
     *
549
     * @param bool $onlyPersistentArguments
550
     * @return SearchRequest
551
     */
552 2
    public function getCopyForSubRequest($onlyPersistentArguments = true)
553
    {
554 2
        if (!$onlyPersistentArguments) {
555
            // create a new request with all data
556
            $argumentsArray = $this->argumentsAccessor->getData();
557
            return new SearchRequest(
558
                $argumentsArray,
559
                $this->contextPageUid,
560
                $this->contextSystemLanguageUid,
561
                $this->contextTypoScriptConfiguration
562
            );
563
        }
564
565 2
        $arguments = new ArrayAccessor();
566 2
        foreach ($this->persistentArgumentsPaths as $persistentArgumentPath) {
567 2
            if ($this->argumentsAccessor->has($persistentArgumentPath)) {
568 2
                $arguments->set($persistentArgumentPath, $this->argumentsAccessor->get($persistentArgumentPath));
569 2
            }
570 2
        }
571
572 2
        return new SearchRequest(
573 2
            $arguments->getData(),
574 2
            $this->contextPageUid,
575 2
            $this->contextSystemLanguageUid,
576 2
            $this->contextTypoScriptConfiguration
577 2
        );
578
    }
579
580
    /**
581
     * @return array
582
     */
583 8
    public function getAsArray()
584
    {
585 8
        return $this->argumentsAccessor->getData();
586
    }
587
}
588