Passed
Pull Request — master (#2944)
by Rafael
66:49 queued 21:50
created

UrlFacetDataBag::setActiveFacets()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets;
4
5
/**
6
 * Copyright notice
7
 *
8
 * (c) 2020 Lars Tode <[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 3 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
28
use ApacheSolrForTypo3\Solr\System\Util\ArrayAccessor;
29
use ApacheSolrForTypo3\Solr\Utility\ParameterSortingUtility;
30
31
/**
32
 * Data bag for facets inside of an url
33
 *
34
 * @author Lars Tode <[email protected]>
35
 * @api
36
 */
37
class UrlFacetDataBag implements \Countable
38
{
39
    /**
40
     * Parameters array has a numeric index
41
     */
42
    const PARAMETER_STYLE_INDEX = 'index';
43
44
    /**
45
     * Parameters array uses combination of key and value as index
46
     */
47
    const PARAMETER_STYLE_ASSOC = 'assoc';
48
49
    /**
50
     * Used parameter style
51
     *
52
     * @var string
53
     */
54
    protected $parameterStyle = self::PARAMETER_STYLE_INDEX;
55
56
    /**
57
     * Argument namespace as configures in TypoScript
58
     *
59
     * @var string
60
     */
61
    protected $argumentNameSpace = 'tx_solr';
62
63
    /**
64
     * @var ArrayAccessor
65
     */
66
    protected $argumentsAccessor;
67
68
    /**
69
     * Mark the data bag as changed
70
     *
71
     * @var bool
72
     */
73
    protected $changed = false;
74
75
    /**
76
     * Parameters should be sorted
77
     *
78
     * @var bool
79
     */
80
    protected $sort = false;
81
82
    /**
83
     * UrlFacetDataBag constructor.
84
     * 
85
     * @param ArrayAccessor $argumentsAccessor
86
     * @param string $argumentNameSpace
87
     * @param string $parameterStyle
88
     */
89
    public function __construct(
90
        ArrayAccessor $argumentsAccessor,
91
        string $argumentNameSpace = 'tx_solr',
92
        string $parameterStyle = self::PARAMETER_STYLE_INDEX
93
    ) {
94
        // Take care that the url style matches in case and is one of the allowed values
95
        $parameterStyle = strtolower(trim($parameterStyle));
96
        if (empty($parameterStyle) ||
97
            (!in_array($parameterStyle, [self::PARAMETER_STYLE_INDEX, self::PARAMETER_STYLE_ASSOC]))) {
98
            $parameterStyle = self::PARAMETER_STYLE_INDEX;
99
        }
100
        $this->argumentsAccessor = $argumentsAccessor;
101
        $this->argumentNameSpace = $argumentNameSpace;
102
        $this->parameterStyle = $parameterStyle;
103
104
        if ($this->parameterStyle === self::PARAMETER_STYLE_ASSOC) {
105
            $this->sort = true;
106
        }
107
    }
108
109
    /**
110
     * Enable the sort of URL parameters
111
     *
112
     * @return $this
113
     */
114
    public function enableSort(): self
115
    {
116
        $this->sort = true;
117
118
        return $this;
119
    }
120
121
    /**
122
     * Disable the sort of URL parameters
123
     *
124
     * @return $this
125
     */
126
    public function disableSort(): self
127
    {
128
        // If the parameter style is assoc, all parameters have to be sorted!
129
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
130
            $this->sort = false;
131
        }
132
133
        return $this;
134
    }
135
136
    /**
137
     * Returns the information if the parameters are sorted
138
     *
139
     * @return bool
140
     */
141
    public function isSorted(): bool
142
    {
143
        return $this->sort;
144
    }
145
146
    /**
147
     * @return string
148
     */
149
    public function getParameterStyle(): string
150
    {
151
        return $this->parameterStyle;
152
    }
153
154
    /**
155
     * Helper method to prefix an accessor with the arguments namespace.
156
     *
157
     * @param string $path
158
     * @return string
159
     */
160
    protected function prefixWithNamespace(string $path = 'filter'): string
161
    {
162
        return $this->argumentNameSpace . ':' . $path;
163
    }
164
165
    /**
166
     * Returns the list of activate facet names
167
     *
168
     * @return array
169
     */
170
    public function getActiveFacetNames(): array
171
    {
172
        $activeFacets = $this->getActiveFacets();
173
        $facetNames = [];
174
175
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
176
            array_map(function($activeFacet) use (&$facetNames) {
177
                $facetNames[] = substr($activeFacet, 0, strpos($activeFacet, ':'));
178
            }, $activeFacets);
179
        } else {
180
            array_map(function($activeFacet) use (&$facetNames) {
181
                $facetNames[] = substr($activeFacet, 0, strpos($activeFacet, ':'));
182
            }, array_keys($activeFacets));
183
        }
184
185
        return $facetNames;
186
    }
187
188
    /**
189
     * Returns all facet values for a certain facetName
190
     * @param string $facetName
191
     * @return array
192
     */
193
    public function getActiveFacetValuesByName(string $facetName): array
194
    {
195
        $values = [];
196
        $activeFacets = $this->getActiveFacets();
197
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
198
            array_map(function($activeFacet) use (&$values, $facetName) {
199
                $parts = explode(':', $activeFacet, 2);
200
                if ($parts[0] === $facetName) {
201
                    $values[] = $parts[1];
202
                }
203
            }, $activeFacets);
204
        } else {
205
            array_map(function($activeFacet) use (&$values, $facetName) {
206
                $parts = explode(':', $activeFacet, 2);
207
                if ($parts[0] === $facetName) {
208
                    $values[] = $parts[1];
209
                }
210
            }, array_keys($activeFacets));
211
        }
212
213
        return $values;
214
    }
215
216
    /**
217
     * Returns the active facets
218
     *
219
     * @return array
220
     */
221
    public function getActiveFacets(): array
222
    {
223
        $path = $this->prefixWithNamespace('filter');
224
        $pathValue = $this->argumentsAccessor->get($path, []);
225
226
        if (!is_array($pathValue)) {
227
            $pathValue = [];
228
        }
229
230
        // Sort url parameter
231
        if ($this->sort) {
232
            ParameterSortingUtility::sortByType(
233
                $pathValue,
234
                $this->parameterStyle
235
            );
236
        }
237
238
        return $pathValue;
239
    }
240
241
    /**
242
     * Returns the active count of facets
243
     *
244
     * @return int
245
     */
246
    public function count(): int
247
    {
248
        return count($this->getActiveFacets());
249
    }
250
251
    /**
252
     * Sets and overwrite the active facets
253
     *
254
     * @param array $activeFacets
255
     *
256
     * @return UrlFacetDataBag
257
     */
258
    public function setActiveFacets(array $activeFacets = []): UrlFacetDataBag
259
    {
260
        $path = $this->prefixWithNamespace('filter');
261
        $this->argumentsAccessor->set($path, $activeFacets);
262
263
        return $this;
264
    }
265
266
    /**
267
     * Adds a facet value to the request.
268
     *
269
     * @param string $facetName
270
     * @param mixed $facetValue
271
     *
272
     * @return UrlFacetDataBag
273
     */
274
    public function addFacetValue(string $facetName, $facetValue): UrlFacetDataBag
275
    {
276
        if ($this->hasFacetValue($facetName, $facetValue)) {
277
            return $this;
278
        }
279
280
        $facetValues = $this->getActiveFacets();
281
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
282
            $facetValues[] = $facetName . ':' . $facetValue;
283
        } else {
284
            $facetValues[$facetName . ':' . $facetValue] = 1;
285
        }
286
287
        $this->changed = true;
288
        $this->setActiveFacets($facetValues);
289
290
        return $this;
291
    }
292
293
    /**
294
     * Removes a facet value from the request.
295
     *
296
     * @param string $facetName
297
     * @param mixed $facetValue
298
     *
299
     * @return UrlFacetDataBag
300
     */
301
    public function removeFacetValue(string $facetName, $facetValue): UrlFacetDataBag
302
    {
303
        if (!$this->hasFacetValue($facetName, $facetValue)) {
304
            return $this;
305
        }
306
        $facetValues = $this->getActiveFacets();
307
        $facetValueToLookFor = $facetName . ':' . $facetValue;
308
309
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
310
            foreach ($facetValues as $index => $facetValue) {
0 ignored issues
show
introduced by
$facetValue is overwriting one of the parameters of this function.
Loading history...
311
                if ($facetValue === $facetValueToLookFor) {
312
                    unset($facetValues[$index]);
313
                    break;
314
                }
315
            }
316
        } else {
317
            if (isset($facetValues[$facetValueToLookFor])) {
318
                unset($facetValues[$facetValueToLookFor]);
319
            }
320
        }
321
        $this->changed = true;
322
        $this->setActiveFacets($facetValues);
323
324
        return $this;
325
    }
326
327
    /**
328
     * Removes all facet values from the request by a certain facet name
329
     *
330
     * @param string $facetName
331
     *
332
     * @return UrlFacetDataBag
333
     */
334
    public function removeAllFacetValuesByName(string $facetName): UrlFacetDataBag
335
    {
336
        $facetValues = $this->getActiveFacets();
337
        $filterOptions = 0;
338
        if ($this->parameterStyle === self::PARAMETER_STYLE_ASSOC) {
339
            $filterOptions = ARRAY_FILTER_USE_KEY;
340
        }
341
342
        $facetValues = array_filter($facetValues, function($facetNameValue) use ($facetName) {
343
            $parts = explode(':', $facetNameValue, 2);
344
            return $parts[0] !== $facetName;
345
        }, $filterOptions);
346
347
        $this->changed = true;
348
        $this->setActiveFacets($facetValues);
349
350
        return $this;
351
    }
352
353
    /**
354
     * Removes all active facets from the request.
355
     *
356
     * @return UrlFacetDataBag
357
     */
358
    public function removeAllFacets(): UrlFacetDataBag
359
    {
360
        $path = $this->prefixWithNamespace('filter');
361
        $this->argumentsAccessor->reset($path);
362
        $this->changed = true;
363
        return $this;
364
    }
365
366
    /**
367
     * Test if there is a active facet with a given value
368
     *
369
     * @param string $facetName
370
     * @param mixed $facetValue
371
     * @return bool
372
     */
373
    public function hasFacetValue(string $facetName, $facetValue): bool
374
    {
375
        $facetNameAndValueToCheck = $facetName . ':' . $facetValue;
376
        $facetValues = $this->getActiveFacets();
377
378
        if ($this->parameterStyle === self::PARAMETER_STYLE_INDEX) {
379
            return in_array($facetNameAndValueToCheck, $this->getActiveFacets());
380
        } else {
381
            return isset($facetValues[$facetNameAndValueToCheck]) && (int)$facetValues[$facetNameAndValueToCheck] === 1;
382
        }
383
    }
384
385
    /**
386
     * Returns the information if the data bag has changes
387
     *
388
     * @return bool
389
     */
390
    public function hasChanged(): bool
391
    {
392
        return $this->changed;
393
    }
394
395
    /**
396
     * Resets the internal change status by explicit acknowledge the change
397
     *
398
     * @return $this
399
     */
400
    public function acknowledgeChange(): UrlFacetDataBag
401
    {
402
        $this->changed = false;
403
404
        return $this;
405
    }
406
}
407