Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
87.04% |
141 / 162 |
|
80.65% |
25 / 31 |
CRAP | |
0.00% |
0 / 1 |
UrlQueryHelper | |
87.04% |
141 / 162 |
|
80.65% |
25 / 31 |
95.29 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getBasicSearchParam | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
clearSearchQueryParams | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
3.03 | |||
regenerateSearchQueryParams | |
73.53% |
25 / 34 |
|
0.00% |
0 / 1 |
19.17 | |||
getDefault | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDefaultParameter | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getParamsWithConfiguredDefaults | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSuppressQuery | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
isQuerySuppressed | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getParamArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
replaceTerm | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
addFacet | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
addFilter | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
removeAllFilters | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
resetDefaultFilters | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
parseFilter | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getAliasesForFacetField | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
removeFacet | |
89.47% |
17 / 19 |
|
0.00% |
0 / 1 |
9.09 | |||
removeFilter | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSort | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
setHandler | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setViewParam | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setLimit | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
setSearchTerms | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
asHiddenFields | |
61.54% |
8 / 13 |
|
0.00% |
0 / 1 |
8.05 | |||
buildQueryString | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
filtered | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
updateQueryString | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
5.07 |
1 | <?php |
2 | |
3 | /** |
4 | * Class to help build URLs and forms in the view based on search settings. |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2010. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Search |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org Main Site |
28 | */ |
29 | |
30 | namespace VuFind\Search; |
31 | |
32 | use VuFindSearch\Query\AbstractQuery; |
33 | use VuFindSearch\Query\Query; |
34 | use VuFindSearch\Query\QueryGroup; |
35 | use VuFindSearch\Query\WorkKeysQuery; |
36 | |
37 | use function call_user_func; |
38 | use function count; |
39 | use function in_array; |
40 | use function is_array; |
41 | use function is_callable; |
42 | |
43 | /** |
44 | * Class to help build URLs and forms in the view based on search settings. |
45 | * |
46 | * @category VuFind |
47 | * @package Search |
48 | * @author Demian Katz <demian.katz@villanova.edu> |
49 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
50 | * @link https://vufind.org Main Site |
51 | */ |
52 | class UrlQueryHelper |
53 | { |
54 | /** |
55 | * Configuration for this helper. |
56 | * |
57 | * @var array |
58 | */ |
59 | protected $config; |
60 | |
61 | /** |
62 | * URL query parameters |
63 | * |
64 | * @var array |
65 | */ |
66 | protected $urlParams = []; |
67 | |
68 | /** |
69 | * Current query object |
70 | * |
71 | * @var AbstractQuery |
72 | */ |
73 | protected $queryObject; |
74 | |
75 | /** |
76 | * Constructor |
77 | * |
78 | * Note that the constructor is final here, because this class relies on |
79 | * "new static()" to build instances, and we must ensure that child classes |
80 | * have consistent constructor signatures. |
81 | * |
82 | * @param array $urlParams Array of URL query parameters. |
83 | * @param AbstractQuery $query Query object to use to update |
84 | * URL query. |
85 | * @param array $options Configuration options for the |
86 | * object. |
87 | * @param bool $regenerateQueryParams Should we add parameters based |
88 | * on the contents of $query to $urlParams (true) or are they already there |
89 | * (false)? |
90 | */ |
91 | final public function __construct( |
92 | array $urlParams, |
93 | AbstractQuery $query, |
94 | array $options = [], |
95 | $regenerateQueryParams = true |
96 | ) { |
97 | $this->config = $options; |
98 | $this->urlParams = $urlParams; |
99 | $this->queryObject = $query; |
100 | if ($regenerateQueryParams) { |
101 | $this->regenerateSearchQueryParams(); |
102 | } |
103 | } |
104 | |
105 | /** |
106 | * Get the name of the basic search param. |
107 | * |
108 | * @return string |
109 | */ |
110 | protected function getBasicSearchParam() |
111 | { |
112 | return $this->config['basicSearchParam'] ?? 'lookfor'; |
113 | } |
114 | |
115 | /** |
116 | * Reset search-related parameters in the internal array. |
117 | * |
118 | * @return void |
119 | */ |
120 | protected function clearSearchQueryParams() |
121 | { |
122 | unset($this->urlParams[$this->getBasicSearchParam()]); |
123 | unset($this->urlParams['join']); |
124 | unset($this->urlParams['type']); |
125 | $searchParams = ['bool', 'lookfor', 'type', 'op']; |
126 | foreach (array_keys($this->urlParams) as $key) { |
127 | if (preg_match('/(' . implode('|', $searchParams) . ')[0-9]+/', $key)) { |
128 | unset($this->urlParams[$key]); |
129 | } |
130 | } |
131 | } |
132 | |
133 | /** |
134 | * Adjust the internal query array based on the query object. |
135 | * |
136 | * @return void |
137 | */ |
138 | protected function regenerateSearchQueryParams() |
139 | { |
140 | $this->clearSearchQueryParams(); |
141 | if ($this->isQuerySuppressed()) { |
142 | return; |
143 | } |
144 | if ($this->queryObject instanceof QueryGroup) { |
145 | $this->urlParams['join'] = $this->queryObject->getOperator(); |
146 | foreach ($this->queryObject->getQueries() as $i => $current) { |
147 | if ($current instanceof QueryGroup) { |
148 | $operator = $current->isNegated() |
149 | ? 'NOT' : $current->getOperator(); |
150 | $this->urlParams['bool' . $i] = [$operator]; |
151 | foreach ($current->getQueries() as $inner) { |
152 | if (!isset($this->urlParams['lookfor' . $i])) { |
153 | $this->urlParams['lookfor' . $i] = []; |
154 | } |
155 | if (!isset($this->urlParams['type' . $i])) { |
156 | $this->urlParams['type' . $i] = []; |
157 | } |
158 | $this->urlParams['lookfor' . $i][] = $inner->getString(); |
159 | $this->urlParams['type' . $i][] = $inner->getHandler(); |
160 | if (null !== ($op = $inner->getOperator())) { |
161 | // We want the op and lookfor parameters to align |
162 | // with each other; let's backfill empty op values |
163 | // if there aren't enough in place already. |
164 | $expectedOps |
165 | = count($this->urlParams['lookfor' . $i]) - 1; |
166 | while ( |
167 | count($this->urlParams['op' . $i] ?? []) |
168 | < $expectedOps |
169 | ) { |
170 | $this->urlParams['op' . $i][] = ''; |
171 | } |
172 | $this->urlParams['op' . $i][] = $op; |
173 | } |
174 | } |
175 | } |
176 | } |
177 | } elseif ($this->queryObject instanceof Query) { |
178 | $search = $this->queryObject->getString(); |
179 | if (!empty($search)) { |
180 | $this->urlParams[$this->getBasicSearchParam()] = $search; |
181 | } |
182 | $type = $this->queryObject->getHandler(); |
183 | if (!empty($type)) { |
184 | $this->urlParams['type'] = $type; |
185 | } |
186 | } elseif ($this->queryObject instanceof WorkKeysQuery) { |
187 | $this->urlParams['id'] = $this->queryObject->getId(); |
188 | $this->urlParams['search'] = 'versions'; |
189 | } |
190 | } |
191 | |
192 | /** |
193 | * Look up a default value in the internal configuration array. |
194 | * |
195 | * @param string $key Name of default to load |
196 | * |
197 | * @return mixed |
198 | */ |
199 | protected function getDefault($key) |
200 | { |
201 | return $this->config['defaults'][$key] ?? null; |
202 | } |
203 | |
204 | /** |
205 | * Set the default value of a parameter, and add that parameter to the object |
206 | * if it is not already defined. |
207 | * |
208 | * @param string $name Name of parameter |
209 | * @param string $value Value of parameter |
210 | * @param bool $forceOverride Force an override of the existing value, even if |
211 | * it was set in the incoming $urlParams in the constructor (defaults to false) |
212 | * |
213 | * @return UrlQueryHelper |
214 | */ |
215 | public function setDefaultParameter($name, $value, $forceOverride = false) |
216 | { |
217 | // Add the new default to the configuration, and apply it to the query |
218 | // if no existing value has already been set in this position (or if an |
219 | // override has been forced). |
220 | $this->config['defaults'][$name] = $value; |
221 | if (!isset($this->urlParams[$name]) || $forceOverride) { |
222 | $this->urlParams[$name] = $value; |
223 | } |
224 | return $this; |
225 | } |
226 | |
227 | /** |
228 | * Get an array of field names with configured defaults; this is a useful way |
229 | * to identify custom query parameters added through setDefaultParameter(). |
230 | * |
231 | * @return array |
232 | */ |
233 | public function getParamsWithConfiguredDefaults() |
234 | { |
235 | return array_keys($this->config['defaults'] ?? []); |
236 | } |
237 | |
238 | /** |
239 | * Control query suppression |
240 | * |
241 | * @param bool $suppress Should we suppress queries? |
242 | * |
243 | * @return UrlQueryHelper |
244 | */ |
245 | public function setSuppressQuery($suppress) |
246 | { |
247 | $this->config['suppressQuery'] = $suppress; |
248 | $this->regenerateSearchQueryParams(); |
249 | return $this; |
250 | } |
251 | |
252 | /** |
253 | * Is query suppressed? |
254 | * |
255 | * @return bool |
256 | */ |
257 | public function isQuerySuppressed() |
258 | { |
259 | return isset($this->config['suppressQuery']) |
260 | ? (bool)$this->config['suppressQuery'] : false; |
261 | } |
262 | |
263 | /** |
264 | * Get an array of URL parameters. |
265 | * |
266 | * @return array |
267 | */ |
268 | public function getParamArray() |
269 | { |
270 | return $this->urlParams; |
271 | } |
272 | |
273 | /** |
274 | * Magic method: behavior when this object is treated as a string. |
275 | * |
276 | * @return string |
277 | */ |
278 | public function __toString() |
279 | { |
280 | $escape = $this->config['escape'] ?? true; |
281 | return $this->getParams($escape); |
282 | } |
283 | |
284 | /** |
285 | * Replace a term in the search query (used for spelling replacement) |
286 | * |
287 | * @param string $from Search term to find |
288 | * @param string $to Search term to insert |
289 | * @param callable $normalizer Function to normalize text strings (null for |
290 | * no normalization) |
291 | * |
292 | * @return UrlQueryHelper |
293 | */ |
294 | public function replaceTerm($from, $to, $normalizer = null) |
295 | { |
296 | $query = clone $this->queryObject; |
297 | $query->replaceTerm($from, $to, $normalizer); |
298 | return new static($this->urlParams, $query, $this->config); |
299 | } |
300 | |
301 | /** |
302 | * Add a facet to the parameters. |
303 | * |
304 | * @param string $field Facet field |
305 | * @param string $value Facet value |
306 | * @param string $operator Facet type to add (AND, OR, NOT) |
307 | * |
308 | * @return UrlQueryHelper |
309 | */ |
310 | public function addFacet($field, $value, $operator = 'AND') |
311 | { |
312 | // Facets are just a special case of filters: |
313 | $prefix = ($operator == 'NOT') ? '-' : ($operator == 'OR' ? '~' : ''); |
314 | return $this->addFilter($prefix . $field . ':"' . $value . '"'); |
315 | } |
316 | |
317 | /** |
318 | * Add a filter to the parameters. |
319 | * |
320 | * @param string $filter Filter to add |
321 | * |
322 | * @return UrlQueryHelper |
323 | */ |
324 | public function addFilter($filter) |
325 | { |
326 | $params = $this->urlParams; |
327 | |
328 | // Add the filter: |
329 | if (!isset($params['filter'])) { |
330 | $params['filter'] = []; |
331 | } |
332 | $params['filter'][] = $filter; |
333 | |
334 | // Clear page: |
335 | unset($params['page']); |
336 | |
337 | return new static($params, $this->queryObject, $this->config, false); |
338 | } |
339 | |
340 | /** |
341 | * Remove all filters. |
342 | * |
343 | * @return string |
344 | */ |
345 | public function removeAllFilters() |
346 | { |
347 | $params = $this->urlParams; |
348 | // Clear page: |
349 | unset($params['filter']); |
350 | |
351 | return new static($params, $this->queryObject, $this->config, false); |
352 | } |
353 | |
354 | /** |
355 | * Reset default filter state. |
356 | * |
357 | * @return string |
358 | */ |
359 | public function resetDefaultFilters() |
360 | { |
361 | $params = $this->urlParams; |
362 | // Clear page: |
363 | unset($params['dfApplied']); |
364 | |
365 | return new static($params, $this->queryObject, $this->config, false); |
366 | } |
367 | |
368 | /** |
369 | * Get the current search parameters as a GET query. |
370 | * |
371 | * @param bool $escape Should we escape the string for use in the view? |
372 | * |
373 | * @return string |
374 | */ |
375 | public function getParams($escape = true) |
376 | { |
377 | return '?' . static::buildQueryString($this->urlParams, $escape); |
378 | } |
379 | |
380 | /** |
381 | * Parse apart the field and value from a URL filter string. |
382 | * |
383 | * @param string $filter A filter string from url : "field:value" |
384 | * |
385 | * @return array Array with elements 0 = field, 1 = value. |
386 | */ |
387 | protected function parseFilter($filter) |
388 | { |
389 | // Simplistic explode/trim behavior if no callback is provided: |
390 | if ( |
391 | !isset($this->config['parseFilterCallback']) |
392 | || !is_callable($this->config['parseFilterCallback']) |
393 | ) { |
394 | $parts = explode(':', $filter, 2); |
395 | $parts[1] = trim($parts[1], '"'); |
396 | return $parts; |
397 | } |
398 | return call_user_func($this->config['parseFilterCallback'], $filter); |
399 | } |
400 | |
401 | /** |
402 | * Given a facet field, return an array containing all aliases of that |
403 | * field. |
404 | * |
405 | * @param string $field Field to look up |
406 | * |
407 | * @return array |
408 | */ |
409 | protected function getAliasesForFacetField($field) |
410 | { |
411 | // If no callback is provided, aliases are unsupported: |
412 | if ( |
413 | !isset($this->config['getAliasesForFacetFieldCallback']) |
414 | || !is_callable($this->config['getAliasesForFacetFieldCallback']) |
415 | ) { |
416 | return [$field]; |
417 | } |
418 | return call_user_func( |
419 | $this->config['getAliasesForFacetFieldCallback'], |
420 | $field |
421 | ); |
422 | } |
423 | |
424 | /** |
425 | * Remove a facet from the parameters. |
426 | * |
427 | * @param string $field Facet field |
428 | * @param string $value Facet value |
429 | * @param string $operator Facet type to add (AND, OR, NOT) |
430 | * |
431 | * @return UrlQueryHelper |
432 | */ |
433 | public function removeFacet($field, $value, $operator = 'AND') |
434 | { |
435 | $params = $this->urlParams; |
436 | |
437 | // Account for operators: |
438 | if ($operator == 'NOT') { |
439 | $field = '-' . $field; |
440 | } elseif ($operator == 'OR') { |
441 | $field = '~' . $field; |
442 | } |
443 | |
444 | $fieldAliases = $this->getAliasesForFacetField($field); |
445 | |
446 | // Remove the filter: |
447 | $newFilter = []; |
448 | if (isset($params['filter']) && is_array($params['filter'])) { |
449 | foreach ($params['filter'] as $current) { |
450 | [$currentField, $currentValue] |
451 | = $this->parseFilter($current); |
452 | if ( |
453 | !in_array($currentField, $fieldAliases) |
454 | || $currentValue != $value |
455 | ) { |
456 | $newFilter[] = $current; |
457 | } |
458 | } |
459 | } |
460 | if (empty($newFilter)) { |
461 | unset($params['filter']); |
462 | } else { |
463 | $params['filter'] = $newFilter; |
464 | } |
465 | |
466 | // Clear page: |
467 | unset($params['page']); |
468 | |
469 | return new static($params, $this->queryObject, $this->config, false); |
470 | } |
471 | |
472 | /** |
473 | * Remove a filter from the parameters. |
474 | * |
475 | * @param string $filter Filter to add |
476 | * |
477 | * @return string |
478 | */ |
479 | public function removeFilter($filter) |
480 | { |
481 | // Treat this as a special case of removeFacet: |
482 | [$field, $value] = $this->parseFilter($filter); |
483 | return $this->removeFacet($field, $value); |
484 | } |
485 | |
486 | /** |
487 | * Return HTTP parameters to render a different page of results. |
488 | * |
489 | * @param string $p New page parameter (null for NO page parameter) |
490 | * |
491 | * @return string |
492 | */ |
493 | public function setPage($p) |
494 | { |
495 | return $this->updateQueryString('page', $p, 1); |
496 | } |
497 | |
498 | /** |
499 | * Return HTTP parameters to render the current page with a different sort |
500 | * parameter. |
501 | * |
502 | * @param string $s New sort parameter (null for NO sort parameter) |
503 | * |
504 | * @return string |
505 | */ |
506 | public function setSort($s) |
507 | { |
508 | return $this->updateQueryString( |
509 | 'sort', |
510 | $s, |
511 | $this->getDefault('sort'), |
512 | true |
513 | ); |
514 | } |
515 | |
516 | /** |
517 | * Return HTTP parameters to render the current page with a different search |
518 | * handler. |
519 | * |
520 | * @param string $handler new Handler. |
521 | * |
522 | * @return string |
523 | */ |
524 | public function setHandler($handler) |
525 | { |
526 | $query = clone $this->queryObject; |
527 | // We can only set the handler on basic queries: |
528 | if ($query instanceof Query) { |
529 | $query->setHandler($handler); |
530 | } |
531 | return new static($this->urlParams, $query, $this->config); |
532 | } |
533 | |
534 | /** |
535 | * Return HTTP parameters to render the current page with a different view |
536 | * parameter. |
537 | * |
538 | * Note: This is called setViewParam rather than setView to avoid confusion |
539 | * with the \Laminas\View\Helper\AbstractHelper interface. |
540 | * |
541 | * @param string $v New sort parameter (null for NO view parameter) |
542 | * |
543 | * @return string |
544 | */ |
545 | public function setViewParam($v) |
546 | { |
547 | // Because of the way view settings are stored in the session, we always |
548 | // want an explicit value here (hence null rather than default view in |
549 | // third parameter below): |
550 | return $this->updateQueryString('view', $v, null); |
551 | } |
552 | |
553 | /** |
554 | * Return HTTP parameters to render the current page with a different limit |
555 | * parameter. |
556 | * |
557 | * @param string $l New limit parameter (null for NO limit parameter) |
558 | * |
559 | * @return string |
560 | */ |
561 | public function setLimit($l) |
562 | { |
563 | return $this->updateQueryString( |
564 | 'limit', |
565 | $l, |
566 | $this->getDefault('limit'), |
567 | true |
568 | ); |
569 | } |
570 | |
571 | /** |
572 | * Return HTTP parameters to render the current page with a different set |
573 | * of search terms. |
574 | * |
575 | * @param string $lookfor New search terms |
576 | * |
577 | * @return string |
578 | */ |
579 | public function setSearchTerms($lookfor) |
580 | { |
581 | $query = new Query($lookfor); |
582 | return new static($this->urlParams, $query, $this->config); |
583 | } |
584 | |
585 | /** |
586 | * Turn the current GET parameters into a set of hidden form fields. |
587 | * |
588 | * @param array $filter Array of parameters to exclude -- key = field name, |
589 | * value = regular expression to exclude. |
590 | * |
591 | * @return string |
592 | */ |
593 | public function asHiddenFields($filter = []) |
594 | { |
595 | $retVal = ''; |
596 | foreach ($this->urlParams as $paramName => $paramValue) { |
597 | if (is_array($paramValue)) { |
598 | foreach ($paramValue as $paramValue2) { |
599 | if (!$this->filtered($paramName, $paramValue2, $filter)) { |
600 | $retVal .= '<input type="hidden" name="' . |
601 | htmlspecialchars($paramName) . '[]" value="' . |
602 | htmlspecialchars($paramValue2 ?? '') . '">'; |
603 | } |
604 | } |
605 | } else { |
606 | if (!$this->filtered($paramName, $paramValue, $filter)) { |
607 | $retVal .= '<input type="hidden" name="' . |
608 | htmlspecialchars($paramName) . '" value="' . |
609 | htmlspecialchars($paramValue ?? '') . '">'; |
610 | } |
611 | } |
612 | } |
613 | return $retVal; |
614 | } |
615 | |
616 | /** |
617 | * Turn an array into a properly URL-encoded query string. This is |
618 | * equivalent to the built-in PHP http_build_query function, but it handles |
619 | * arrays in a more compact way and ensures that ampersands don't get |
620 | * messed up based on server-specific settings. |
621 | * |
622 | * @param array $a Array of parameters to turn into a GET string |
623 | * @param bool $escape Should we escape the string for use in the view? |
624 | * |
625 | * @return string |
626 | */ |
627 | public static function buildQueryString($a, $escape = true) |
628 | { |
629 | $parts = []; |
630 | foreach ($a as $key => $value) { |
631 | if (is_array($value)) { |
632 | foreach ($value as $current) { |
633 | $parts[] = urlencode($key . '[]') . '=' . urlencode($current ?? ''); |
634 | } |
635 | } else { |
636 | $parts[] = urlencode($key) . '=' . urlencode($value ?? ''); |
637 | } |
638 | } |
639 | $retVal = implode('&', $parts); |
640 | return $escape ? htmlspecialchars($retVal) : $retVal; |
641 | } |
642 | |
643 | /** |
644 | * Support method for asHiddenFields -- are the provided field and value |
645 | * excluded by the provided filter? |
646 | * |
647 | * @param string $field Field to check |
648 | * @param string $value Regular expression to check |
649 | * @param array $filter Filter provided to asHiddenFields() above |
650 | * |
651 | * @return bool |
652 | */ |
653 | protected function filtered($field, $value, $filter) |
654 | { |
655 | return isset($filter[$field]) && preg_match($filter[$field], $value); |
656 | } |
657 | |
658 | /** |
659 | * Generic case of parameter rebuilding. |
660 | * |
661 | * @param string $field Field to update |
662 | * @param string $value Value to use (null to skip field entirely) |
663 | * @param string $default Default value (skip field if $value matches; null |
664 | * for no default). |
665 | * @param bool $clearPage Should we clear the page number, if any? |
666 | * |
667 | * @return string |
668 | */ |
669 | protected function updateQueryString( |
670 | $field, |
671 | $value, |
672 | $default = null, |
673 | $clearPage = false |
674 | ) { |
675 | $params = $this->urlParams; |
676 | if (null === $value || $value == $default) { |
677 | unset($params[$field]); |
678 | } else { |
679 | $params[$field] = $value; |
680 | } |
681 | if ($clearPage && isset($params['page'])) { |
682 | unset($params['page']); |
683 | } |
684 | return new static($params, $this->queryObject, $this->config, false); |
685 | } |
686 | } |