Passed
Push — master ( 5adbd6...5901ba )
by Timo
20:55
created

SearchRequest::removeFacetValue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

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