Completed
Branch master (33af51)
by Timo
07:57
created

SearchRequest::setActiveFacets()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
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 = array('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 56
    public function __construct(array $argumentsArray = [], $pageUid = 0, $sysLanguageUid = 0, TypoScriptConfiguration $typoScriptConfiguration = null)
102
    {
103 56
        $this->stateChanged = true;
104 56
        $this->persistedArguments = $argumentsArray;
105 56
        $this->contextPageUid = $pageUid;
106 56
        $this->contextSystemLanguageUid = $sysLanguageUid;
107 56
        $this->contextTypoScriptConfiguration = $typoScriptConfiguration;
108 56
        $this->id = spl_object_hash($this);
109 56
        $this->reset();
110 56
    }
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 47
    protected function prefixWithNamespace($path)
145
    {
146 47
        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 integer $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, null);
0 ignored issues
show
Unused Code introduced by
The call to ArrayAccessor::reset() has too many arguments starting with null.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
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 32
    public function getPage()
413
    {
414 32
        $path = $this->prefixWithNamespace('page');
415 32
        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 9
    public function setRawQueryString($rawQueryString)
425
    {
426 9
        $this->stateChanged = true;
427 9
        $this->argumentsAccessor->set('q', $rawQueryString);
428 9
        return $this;
429
    }
430
431
    /**
432
     * Returns the passed rawQueryString.
433
     *
434
     * @return int|string
435
     */
436 31
    public function getRawUserQuery()
437
    {
438 31
        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 33
    public function getRawUserQueryIsEmptyString()
449
    {
450 33
        $query = $this->argumentsAccessor->get('q', null);
451
452 33
        if ($query === null) {
453 2
            return false;
454
        }
455
456 31
        if (trim($query) === '') {
457 4
            return true;
458
        }
459
460 27
        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 33
    public function getRawUserQueryIsNull()
470
    {
471 33
        $query = $this->argumentsAccessor->get('q', null);
472 33
        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 30
    public function getResultsPerPage()
503
    {
504 30
        $path = $this->prefixWithNamespace('resultsPerPage');
505 30
        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 56
    public function reset()
540
    {
541 56
        $this->argumentsAccessor = new ArrayAccessor($this->persistedArguments);
542 56
        $this->stateChanged = false;
543 56
        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($argumentsArray);
558
        }
559
560 2
        $arguments = new ArrayAccessor();
561 2
        foreach ($this->persistentArgumentsPaths as $persistentArgumentPath) {
562 2
            if ($this->argumentsAccessor->has($persistentArgumentPath)) {
563 2
                $arguments->set($persistentArgumentPath, $this->argumentsAccessor->get($persistentArgumentPath));
564 2
            }
565 2
        }
566
567 2
        return new SearchRequest($arguments->getData());
568
    }
569
570
    /**
571
     * @return array
572
     */
573 8
    public function getAsArray()
574
    {
575 8
        return $this->argumentsAccessor->getData();
576
    }
577
}
578