Passed
Pull Request — master (#1317)
by
unknown
21:24
created

SearchRequest::addFacetValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 2
crap 2
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 78
    public function __construct(array $argumentsArray = [], $pageUid = 0, $sysLanguageUid = 0, TypoScriptConfiguration $typoScriptConfiguration = null)
102
    {
103 78
        $this->stateChanged = true;
104 78
        $this->persistedArguments = $argumentsArray;
105 78
        $this->contextPageUid = $pageUid;
106 78
        $this->contextSystemLanguageUid = $sysLanguageUid;
107 78
        $this->contextTypoScriptConfiguration = $typoScriptConfiguration;
108 78
        $this->id = spl_object_hash($this);
109 78
        $this->reset();
110 78
    }
111
112
    /**
113
     * @return string
114
     */
115 20
    public function getId()
116
    {
117 20
        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 1
    public function mergeArguments(array $argumentsToMerge)
127
    {
128 1
        ArrayUtility::mergeRecursiveWithOverrule(
129 1
            $this->persistedArguments,
130 1
            $argumentsToMerge
131
        );
132
133 1
        $this->reset();
134
135 1
        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 55
    protected function prefixWithNamespace($path)
145
    {
146 55
        return $this->argumentNameSpace . ':' . $path;
147
    }
148
149
    /**
150
     * @return array
151
     */
152 20
    public function getActiveFacetNames()
153
    {
154 20
        $activeFacets = $this->getActiveFacets();
155 20
        $facetNames = [];
156
157
        array_map(function($activeFacet) use (&$facetNames) {
158 3
            $facetNames[] = substr($activeFacet, 0, strpos($activeFacet, ':'));
159 20
        }, $activeFacets);
160
161 20
        return $facetNames;
162
    }
163
164
    /**
165
     * Returns all facet values for a certain facetName
166
     * @param string $facetName
167
     * @return array
168
     */
169 30
    public function getActiveFacetValuesByName($facetName)
170
    {
171 30
        $values = [];
172 30
        $activeFacets = $this->getActiveFacets();
173
174
        array_map(function($activeFacet) use (&$values, $facetName) {
175 9
            $parts = explode(':', $activeFacet, 2);
176 9
            if ($parts[0] === $facetName) {
177 9
                $values[] = $parts[1];
178
            }
179 30
        }, $activeFacets);
180
181 30
        return $values;
182
    }
183
184
    /**
185
     * @return array
186
     */
187 37
    protected function getActiveFacets()
188
    {
189 37
        $path = $this->prefixWithNamespace('filter');
190 37
        $pathValue = $this->argumentsAccessor->get($path, []);
191
192 37
        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 31
    protected function setActiveFacets($activeFacets = [])
209
    {
210 31
        $path = $this->prefixWithNamespace('filter');
211 31
        $this->argumentsAccessor->set($path, $activeFacets);
212
213 31
        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 27
    public function addFacetValue($facetName, $facetValue)
225
    {
226 27
        if ($this->getHasFacetValue($facetName, $facetValue)) {
227 2
            return $this;
228
        }
229
230 27
        $facetValues = $this->getActiveFacets();
231 27
        $facetValues[] = $facetName . ':' . $facetValue;
232 27
        $this->setActiveFacets($facetValues);
233
234 27
        $this->stateChanged = true;
235 27
        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 4
    public function removeFacetValue($facetName, $facetValue)
247
    {
248 4
        if (!$this->getHasFacetValue($facetName, $facetValue)) {
249
            return $this;
250
        }
251 4
        $facetValues = $this->getActiveFacets();
252 4
        $facetValueToLookFor = $facetName . ':' . $facetValue;
253
254 4
        foreach ($facetValues as $index => $facetValue) {
255 4
            if ($facetValue === $facetValueToLookFor) {
256 4
                unset($facetValues[$index]);
257 4
                break;
258
            }
259
        }
260
261 4
        $this->setActiveFacets($facetValues);
262 4
        $this->stateChanged = true;
263 4
        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 3
    public function removeAllFacetValuesByName($facetName)
274
    {
275 3
        $facetValues = $this->getActiveFacets();
276 3
        $facetValues = array_filter($facetValues, function($facetValue) use ($facetName) {
277 2
            $parts = explode(':', $facetValue, 2);
278 2
            return $parts[0] !== $facetName;
279 3
        });
280
281 3
        $this->setActiveFacets($facetValues);
282 3
        $this->stateChanged = true;
283 3
        return $this;
284
    }
285
286
    /**
287
     * Removes all active facets from the request.
288
     *
289
     * @return SearchRequest
290
     */
291 4
    public function removeAllFacets()
292
    {
293 4
        $path = $this->prefixWithNamespace('filter');
294 4
        $this->argumentsAccessor->reset($path);
295 4
        $this->stateChanged = true;
296 4
        return $this;
297
    }
298
299
    /**
300
     * @param string $facetName
301
     * @param mixed $facetValue
302
     * @return bool
303
     */
304 30
    public function getHasFacetValue($facetName, $facetValue)
305
    {
306 30
        $facetNameAndValueToCheck = $facetName . ':' . $facetValue;
307 30
        return in_array($facetNameAndValueToCheck, $this->getActiveFacets());
308
    }
309
310
    /**
311
     * @return bool
312
     */
313 35
    public function getHasSorting()
314
    {
315 35
        $path = $this->prefixWithNamespace('sort');
316 35
        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 34
    protected function getSorting()
325
    {
326 34
        $path = $this->prefixWithNamespace('sort');
327 34
        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 34
    protected function getSortingPart($index)
337
    {
338 34
        $sorting = $this->getSorting();
339 34
        if ($sorting === null) {
340 32
            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 34
    public function getSortingName()
353
    {
354 34
        return $this->getSortingPart(0);
355
    }
356
357
    /**
358
     * Returns the sorting direction that is currently used.
359
     *
360
     * @return string
361
     */
362 33
    public function getSortingDirection()
363
    {
364 33
        return strtolower($this->getSortingPart(1));
365
    }
366
367
    /**
368
     * @return SearchRequest
369
     */
370 22
    public function removeSorting()
371
    {
372 22
        $path = $this->prefixWithNamespace('sort');
373 22
        $this->argumentsAccessor->reset($path);
374 22
        $this->stateChanged = true;
375 22
        return $this;
376
    }
377
378
    /**
379
     * @param string $sortingName
380
     * @param string $direction (asc or desc)
381
     *
382
     * @return SearchRequest
383
     */
384 23
    public function setSorting($sortingName, $direction = 'asc')
385
    {
386 23
        $value = $sortingName . ' ' . $direction;
387 23
        $path = $this->prefixWithNamespace('sort');
388 23
        $this->argumentsAccessor->set($path, $value);
389 23
        $this->stateChanged = true;
390 23
        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 9
    public function setPage($page)
400
    {
401 9
        $this->stateChanged = true;
402 9
        $path = $this->prefixWithNamespace('page');
403 9
        $this->argumentsAccessor->set($path, $page);
404 9
        return $this;
405
    }
406
407
    /**
408
     * Returns the passed page.
409
     *
410
     * @return int|null
411
     */
412 34
    public function getPage()
413
    {
414 34
        $path = $this->prefixWithNamespace('page');
415 34
        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 30
    public function setRawQueryString($rawQueryString)
425
    {
426 30
        $this->stateChanged = true;
427 30
        $this->argumentsAccessor->set('q', $rawQueryString);
428 30
        return $this;
429
    }
430
431
    /**
432
     * Returns the passed rawQueryString.
433
     *
434
     * @return string
435
     */
436 35
    public function getRawUserQuery()
437
    {
438 35
        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 32
    public function getRawUserQueryIsEmptyString()
449
    {
450 32
        $query = $this->argumentsAccessor->get('q', null);
451
452 32
        if ($query === null) {
453
            return false;
454
        }
455
456 32
        if (trim($query) === '') {
457
            return true;
458
        }
459
460 32
        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 32
    public function getResultsPerPage()
503
    {
504 32
        $path = $this->prefixWithNamespace('resultsPerPage');
505 32
        return $this->argumentsAccessor->get($path);
506
    }
507
508
    /**
509
     * @return int
510
     */
511 22
    public function getContextSystemLanguageUid()
512
    {
513 22
        return $this->contextSystemLanguageUid;
514
    }
515
516
    /**
517
     * @return int
518
     */
519 22
    public function getContextPageUid()
520
    {
521 22
        return $this->contextPageUid;
522
    }
523
524
    /**
525
     * Get contextTypoScriptConfiguration
526
     *
527
     * @return TypoScriptConfiguration
528
     */
529 41
    public function getContextTypoScriptConfiguration()
530
    {
531 41
        return $this->contextTypoScriptConfiguration;
532
    }
533
534
    /**
535
     * Assigns the last known persistedArguments and restores their state.
536
     *
537
     * @return SearchRequest
538
     */
539 76
    public function reset()
540
    {
541 76
        $this->argumentsAccessor = new ArrayAccessor($this->persistedArguments);
542 76
        $this->stateChanged = false;
543 76
        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 29
    public function getCopyForSubRequest($onlyPersistentArguments = true)
553
    {
554 29
        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 29
        $arguments = new ArrayAccessor();
566 29
        foreach ($this->persistentArgumentsPaths as $persistentArgumentPath) {
567 29
            if ($this->argumentsAccessor->has($persistentArgumentPath)) {
568 25
                $arguments->set($persistentArgumentPath, $this->argumentsAccessor->get($persistentArgumentPath));
569
            }
570
        }
571
572 29
        return new SearchRequest(
573 29
            $arguments->getData(),
574 29
            $this->contextPageUid,
575 29
            $this->contextSystemLanguageUid,
576 29
            $this->contextTypoScriptConfiguration
577
        );
578
    }
579
580
    /**
581
     * @return array
582
     */
583 37
    public function getAsArray()
584
    {
585 37
        return $this->argumentsAccessor->getData();
586
    }
587
}
588