Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.38% |
62 / 65 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
FacetFormatter | |
95.38% |
62 / 65 |
|
50.00% |
2 / 4 |
30 | |
0.00% |
0 / 1 |
buildFacetFilters | |
81.82% |
9 / 11 |
|
0.00% |
0 / 1 |
5.15 | |||
matchFacetItem | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
buildFacetValues | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
9 | |||
format | |
96.30% |
26 / 27 |
|
0.00% |
0 / 1 |
11 |
1 | <?php |
2 | |
3 | /** |
4 | * Facet formatter for API responses |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2015-2016. |
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 API_Formatter |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development:plugins:controllers Wiki |
28 | */ |
29 | |
30 | namespace VuFindApi\Formatter; |
31 | |
32 | use VuFind\Search\Base\Results; |
33 | |
34 | use function in_array; |
35 | |
36 | /** |
37 | * Facet formatter for API responses |
38 | * |
39 | * @category VuFind |
40 | * @package API_Formatter |
41 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
42 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
43 | * @link https://vufind.org/wiki/development:plugins:controllers Wiki |
44 | */ |
45 | class FacetFormatter extends BaseFormatter |
46 | { |
47 | /** |
48 | * Build an array of facet filters from the request params |
49 | * |
50 | * @param array $request Request params |
51 | * |
52 | * @return array |
53 | */ |
54 | protected function buildFacetFilters($request) |
55 | { |
56 | $facetFilters = []; |
57 | if (isset($request['facetFilter'])) { |
58 | foreach ($request['facetFilter'] as $filter) { |
59 | [$facetField, $regex] = explode(':', $filter, 2); |
60 | $regex = trim($regex); |
61 | if (str_starts_with($regex, '"')) { |
62 | $regex = substr($regex, 1); |
63 | } |
64 | if (str_ends_with($regex, '"')) { |
65 | $regex = substr($regex, 0, -1); |
66 | } |
67 | $facetFilters[$facetField][] = $regex; |
68 | } |
69 | } |
70 | return $facetFilters; |
71 | } |
72 | |
73 | /** |
74 | * Match a facet item with the filters. |
75 | * |
76 | * @param array $facet Facet |
77 | * @param array $filters Facet filters |
78 | * |
79 | * @return boolean |
80 | */ |
81 | protected function matchFacetItem($facet, $filters) |
82 | { |
83 | $discard = true; |
84 | array_walk_recursive( |
85 | $facet, |
86 | function ($item, $key) use (&$discard, $filters) { |
87 | if ($discard && $key == 'value') { |
88 | foreach ($filters as $filter) { |
89 | $pattern = '/' . addcslashes($filter, '/') . '/'; |
90 | if (preg_match($pattern, $item) === 1) { |
91 | $discard = false; |
92 | break; |
93 | } |
94 | } |
95 | } |
96 | } |
97 | ); |
98 | return !$discard; |
99 | } |
100 | |
101 | /** |
102 | * Recursive function to create a facet value list for a single facet |
103 | * |
104 | * @param array $list Facet items |
105 | * @param array $filters Facet filters |
106 | * |
107 | * @return array |
108 | */ |
109 | protected function buildFacetValues($list, $filters = false) |
110 | { |
111 | $result = []; |
112 | $fields = [ |
113 | 'value', 'displayText', 'count', |
114 | 'children', 'href', 'isApplied', |
115 | ]; |
116 | foreach ($list as $value) { |
117 | $resultValue = []; |
118 | if ($filters && !$this->matchFacetItem($value, $filters)) { |
119 | continue; |
120 | } |
121 | |
122 | foreach ($value as $key => $item) { |
123 | if (!in_array($key, $fields)) { |
124 | continue; |
125 | } |
126 | if ($key == 'children') { |
127 | if (!empty($item)) { |
128 | $resultValue[$key] |
129 | = $this->buildFacetValues( |
130 | $item, |
131 | $filters |
132 | ); |
133 | } |
134 | } else { |
135 | if ($key == 'displayText') { |
136 | $key = 'translated'; |
137 | } |
138 | $resultValue[$key] = $item; |
139 | } |
140 | } |
141 | $result[] = $resultValue; |
142 | } |
143 | return $result; |
144 | } |
145 | |
146 | /** |
147 | * Create the result facet list |
148 | * |
149 | * @param array $request Request parameters |
150 | * @param Results $results Search results |
151 | * @param array $hierarchicalFacetData Hierarchical facet data |
152 | * |
153 | * @return array |
154 | */ |
155 | public function format($request, Results $results, $hierarchicalFacetData) |
156 | { |
157 | if ($results->getResultTotal() <= 0 || empty($request['facet'])) { |
158 | return []; |
159 | } |
160 | |
161 | $filters = $this->buildFacetFilters($request); |
162 | $facets = $results->getFacetList(); |
163 | |
164 | // Format hierarchical facets, if any |
165 | if ($hierarchicalFacetData) { |
166 | foreach ($hierarchicalFacetData as $facet => $data) { |
167 | $facets[$facet]['list'] = $data; |
168 | } |
169 | } |
170 | |
171 | // Add "missing" fields to non-hierarchical facets to make them similar |
172 | // to hierarchical facets for easier consumption. |
173 | $urlHelper = $results->getUrlQuery(); |
174 | foreach ($facets as $facetKey => &$facetItems) { |
175 | if (isset($hierarchicalFacetData[$facetKey])) { |
176 | continue; |
177 | } |
178 | |
179 | foreach ($facetItems['list'] as &$item) { |
180 | $href = !$item['isApplied'] |
181 | ? $urlHelper->addFacet( |
182 | $facetKey, |
183 | $item['value'], |
184 | $item['operator'] |
185 | )->getParams(false) : $urlHelper->getParams(false); |
186 | $item['href'] = $href; |
187 | } |
188 | } |
189 | $this->filterArrayValues($facets); |
190 | |
191 | $result = []; |
192 | foreach ($facets as $facetName => $facetData) { |
193 | $result[$facetName] = $this->buildFacetValues( |
194 | $facetData['list'], |
195 | !empty($filters[$facetName]) ? $filters[$facetName] : false |
196 | ); |
197 | } |
198 | return $result; |
199 | } |
200 | } |