Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 86 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
GetSideFacets | |
0.00% |
0 / 86 |
|
0.00% |
0 / 5 |
210 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
handleRequest | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
42 | |||
getFacetResults | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
12 | |||
formatFacets | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
12 | |||
getCheckboxFacetCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * "Get Side Facets" AJAX handler |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2018-2023. |
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 AJAX |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @author Juha Luoma <juha.luoma@helsinki.fi> |
27 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
28 | * @link https://vufind.org/wiki/development Wiki |
29 | */ |
30 | |
31 | namespace VuFind\AjaxHandler; |
32 | |
33 | use Laminas\Mvc\Controller\Plugin\Params; |
34 | use Laminas\View\Renderer\RendererInterface; |
35 | use VuFind\Recommend\PluginManager as RecommendPluginManager; |
36 | use VuFind\Recommend\SideFacets; |
37 | use VuFind\Search\Base\Results; |
38 | use VuFind\Search\RecommendListener; |
39 | use VuFind\Search\SearchRunner; |
40 | use VuFind\Session\Settings as SessionSettings; |
41 | |
42 | use function is_callable; |
43 | |
44 | /** |
45 | * "Get Side Facets" AJAX handler |
46 | * |
47 | * @category VuFind |
48 | * @package AJAX |
49 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
50 | * @author Juha Luoma <juha.luoma@helsinki.fi> |
51 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
52 | * @link https://vufind.org/wiki/development Wiki |
53 | */ |
54 | class GetSideFacets extends \VuFind\AjaxHandler\AbstractBase implements \Laminas\Log\LoggerAwareInterface |
55 | { |
56 | use \VuFind\Log\LoggerAwareTrait; |
57 | |
58 | /** |
59 | * Recommend plugin manager |
60 | * |
61 | * @var RecommendPluginManager |
62 | */ |
63 | protected $recommendPluginManager; |
64 | |
65 | /** |
66 | * Search runner |
67 | * |
68 | * @var SearchRunner |
69 | */ |
70 | protected $searchRunner; |
71 | |
72 | /** |
73 | * View renderer |
74 | * |
75 | * @var RendererInterface |
76 | */ |
77 | protected $renderer; |
78 | |
79 | /** |
80 | * Constructor |
81 | * |
82 | * @param SessionSettings $ss Session settings |
83 | * @param RecommendPluginManager $rpm Recommend plugin manager |
84 | * @param SearchRunner $sr Search runner |
85 | * @param RendererInterface $renderer View renderer |
86 | */ |
87 | public function __construct( |
88 | SessionSettings $ss, |
89 | \VuFind\Recommend\PluginManager $rpm, |
90 | SearchRunner $sr, |
91 | RendererInterface $renderer |
92 | ) { |
93 | $this->sessionSettings = $ss; |
94 | $this->recommendPluginManager = $rpm; |
95 | $this->searchRunner = $sr; |
96 | $this->renderer = $renderer; |
97 | } |
98 | |
99 | /** |
100 | * Handle a request. |
101 | * |
102 | * @param Params $params Parameter helper from controller |
103 | * |
104 | * @return array [response data, HTTP status code] |
105 | */ |
106 | public function handleRequest(Params $params) |
107 | { |
108 | $this->disableSessionWrites(); // avoid session write timing bug |
109 | |
110 | // Allow both GET and POST variables: |
111 | $request = $params->fromQuery() + $params->fromPost(); |
112 | |
113 | $configIndex = $request['configIndex'] ?? 0; |
114 | $configLocation = $request['location'] ?? 'side'; |
115 | $results = $this->getFacetResults($request, $configIndex, $configLocation); |
116 | if ($results instanceof \VuFind\Search\EmptySet\Results) { |
117 | $this->logError('Faceting request failed'); |
118 | return $this->formatResponse('', self::STATUS_HTTP_ERROR); |
119 | } |
120 | |
121 | // Set appropriate query suppression / extra field behavior: |
122 | $queryHelper = $results->getUrlQuery(); |
123 | $queryHelper->setSuppressQuery((bool)($request['querySuppressed'] ?? false)); |
124 | $extraFields = array_filter(explode(',', $request['extraFields'] ?? '')); |
125 | foreach ($extraFields as $field) { |
126 | if (isset($request[$field])) { |
127 | $queryHelper->setDefaultParameter($field, $request[$field]); |
128 | } |
129 | } |
130 | |
131 | $recommend = $results->getRecommendations($configLocation)[0] ?? null; |
132 | if (null === $recommend) { |
133 | return $this->formatResponse( |
134 | 'Invalid config requested', |
135 | self::STATUS_HTTP_BAD_REQUEST |
136 | ); |
137 | } |
138 | |
139 | $context = [ |
140 | 'recommend' => $recommend, |
141 | 'params' => $results->getParams(), |
142 | 'searchClassId' => $request['searchClassId'] ?? DEFAULT_SEARCH_BACKEND, |
143 | ]; |
144 | if (isset($request['enabledFacets'])) { |
145 | // Render requested facets separately |
146 | $facets = $this->formatFacets( |
147 | $context, |
148 | $recommend, |
149 | $request['enabledFacets'], |
150 | $results |
151 | ); |
152 | return $this->formatResponse(compact('facets')); |
153 | } |
154 | |
155 | // Render full sidefacets |
156 | $html = $this->renderer->render( |
157 | 'Recommend/SideFacets.phtml', |
158 | $context |
159 | ); |
160 | return $this->formatResponse(compact('html')); |
161 | } |
162 | |
163 | /** |
164 | * Perform search and return the results |
165 | * |
166 | * @param array $request Request params |
167 | * @param string $index Index of SideFacetsDeferred in configuration |
168 | * @param string $loc Location where SideFacetsDeferred is configured |
169 | * |
170 | * @return Results |
171 | */ |
172 | protected function getFacetResults(array $request, $index, $loc) |
173 | { |
174 | $setupCallback = function ($runner, $params, $searchId) use ($index, $loc) { |
175 | $listener = new RecommendListener( |
176 | $this->recommendPluginManager, |
177 | $searchId |
178 | ); |
179 | $config = []; |
180 | $rawConfig = $params->getOptions() |
181 | ->getRecommendationSettings($params->getSearchHandler()); |
182 | $settings = explode(':', $rawConfig[$loc][$index] ?? ''); |
183 | if ($settings[0] === 'SideFacetsDeferred') { |
184 | $settings[0] = 'SideFacets'; |
185 | $config[$loc][] = implode(':', $settings); |
186 | } |
187 | $listener->setConfig($config); |
188 | $listener->attach($runner->getEventManager()->getSharedManager()); |
189 | |
190 | $params->setLimit(0); |
191 | if (is_callable([$params, 'setHierarchicalFacetLimit'])) { |
192 | $params->setHierarchicalFacetLimit(-1); |
193 | } |
194 | $options = $params->getOptions(); |
195 | $options->disableHighlighting(); |
196 | $options->spellcheckEnabled(false); |
197 | }; |
198 | |
199 | $runner = $this->searchRunner; |
200 | return $runner->run( |
201 | $request, |
202 | $request['searchClassId'] ?? DEFAULT_SEARCH_BACKEND, |
203 | $setupCallback |
204 | ); |
205 | } |
206 | |
207 | /** |
208 | * Format facets according to their type |
209 | * |
210 | * @param array $context View rendering context |
211 | * @param SideFacets $recommend Recommendation module |
212 | * @param array $facets Facets to process |
213 | * @param Results $results Search results |
214 | * |
215 | * @return array |
216 | */ |
217 | protected function formatFacets( |
218 | $context, |
219 | SideFacets $recommend, |
220 | $facets, |
221 | Results $results |
222 | ) { |
223 | $response = []; |
224 | $facetSet = $recommend->getFacetSet(); |
225 | foreach ($facets as $facet) { |
226 | if (strpos($facet, ':')) { |
227 | $response[$facet]['checkboxCount'] |
228 | = $this->getCheckboxFacetCount($facet, $results); |
229 | } else { |
230 | $context['facet'] = $facet; |
231 | $context['cluster'] = $facetSet[$facet] ?? [ |
232 | 'list' => [], |
233 | ]; |
234 | $context['collapsedFacets'] = []; |
235 | $response[$facet]['html'] = $this->renderer->render( |
236 | 'Recommend/SideFacets/facet.phtml', |
237 | $context |
238 | ); |
239 | } |
240 | } |
241 | return $response; |
242 | } |
243 | |
244 | /** |
245 | * Get the result count for a checkbox facet |
246 | * |
247 | * @param string $facet Facet |
248 | * @param Results $results Search results |
249 | * |
250 | * @return int|null |
251 | */ |
252 | protected function getCheckboxFacetCount($facet, Results $results) |
253 | { |
254 | // There's currently no good way to return counts for checkbox filters. |
255 | return null; |
256 | } |
257 | } |