Total Complexity | 47 |
Total Lines | 387 |
Duplicated Lines | 0 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Complex classes like SearchUriBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SearchUriBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | class SearchUriBuilder |
||
47 | { |
||
48 | protected ?UriBuilder $uriBuilder = null; |
||
49 | |||
50 | protected static array $preCompiledLinks = []; |
||
51 | |||
52 | protected static int $hitCount = 0; |
||
53 | |||
54 | protected static int $missCount = 0; |
||
55 | |||
56 | protected static array $additionalArgumentsCache = []; |
||
57 | |||
58 | protected EventDispatcherInterface $eventDispatcher; |
||
59 | |||
60 | protected ?RoutingService $routingService = null; |
||
61 | |||
62 | public function injectUriBuilder(UriBuilder $uriBuilder): void |
||
63 | { |
||
64 | $this->uriBuilder = $uriBuilder; |
||
65 | } |
||
66 | |||
67 | public function injectRoutingService(RoutingService $routingService): void |
||
68 | { |
||
69 | $this->routingService = $routingService; |
||
70 | } |
||
71 | |||
72 | public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void |
||
73 | { |
||
74 | $this->eventDispatcher = $eventDispatcher; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * @param mixed $facetValue |
||
79 | */ |
||
80 | public function getAddFacetValueUri(SearchRequest $previousSearchRequest, string $facetName, $facetValue): string |
||
81 | { |
||
82 | $persistentAndFacetArguments = $previousSearchRequest |
||
83 | ->getCopyForSubRequest()->removeAllGroupItemPages()->addFacetValue($facetName, $facetValue) |
||
84 | ->getAsArray(); |
||
85 | |||
86 | $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest); |
||
87 | |||
88 | $arguments = $persistentAndFacetArguments + $additionalArguments; |
||
89 | |||
90 | $this->sortFilterParametersIfNecessary($previousSearchRequest, $arguments); |
||
91 | |||
92 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
93 | return $this->buildLinkWithInMemoryCache($pageUid, $arguments); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Removes all other facet values for this name and only set's the passed value for the facet. |
||
98 | */ |
||
99 | public function getSetFacetValueUri(SearchRequest $previousSearchRequest, $facetName, $facetValue): string |
||
100 | { |
||
101 | $previousSearchRequest = $previousSearchRequest |
||
102 | ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacetValuesByName($facetName); |
||
103 | |||
104 | return $this->getAddFacetValueUri($previousSearchRequest, $facetName, $facetValue); |
||
105 | } |
||
106 | |||
107 | public function getRemoveFacetValueUri(SearchRequest $previousSearchRequest, $facetName, $facetValue): string |
||
108 | { |
||
109 | $persistentAndFacetArguments = $previousSearchRequest |
||
110 | ->getCopyForSubRequest()->removeAllGroupItemPages()->removeFacetValue($facetName, $facetValue) |
||
111 | ->getAsArray(); |
||
112 | |||
113 | $additionalArguments = []; |
||
114 | if ( |
||
115 | ($typoScriptConfiguration = $previousSearchRequest->getContextTypoScriptConfiguration()) |
||
116 | && $typoScriptConfiguration instanceof TypoScriptConfiguration |
||
117 | && $typoScriptConfiguration->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl() |
||
118 | ) { |
||
119 | $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest); |
||
120 | } |
||
121 | $arguments = $persistentAndFacetArguments + $additionalArguments; |
||
122 | |||
123 | $this->sortFilterParametersIfNecessary($previousSearchRequest, $arguments); |
||
124 | |||
125 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
126 | return $this->buildLinkWithInMemoryCache($pageUid, $arguments); |
||
127 | } |
||
128 | |||
129 | public function getRemoveFacetUri(SearchRequest $previousSearchRequest, $facetName): string |
||
130 | { |
||
131 | $persistentAndFacetArguments = $previousSearchRequest |
||
132 | ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacetValuesByName($facetName) |
||
133 | ->getAsArray(); |
||
134 | |||
135 | $additionalArguments = []; |
||
136 | if ( |
||
137 | ($typoScriptConfiguration = $previousSearchRequest->getContextTypoScriptConfiguration()) |
||
138 | && $typoScriptConfiguration instanceof TypoScriptConfiguration |
||
139 | && $typoScriptConfiguration->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl() |
||
140 | ) { |
||
141 | $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest); |
||
142 | } |
||
143 | |||
144 | $arguments = $persistentAndFacetArguments + $additionalArguments; |
||
145 | |||
146 | $this->sortFilterParametersIfNecessary($previousSearchRequest, $arguments); |
||
147 | |||
148 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
149 | return $this->buildLinkWithInMemoryCache($pageUid, $arguments); |
||
150 | } |
||
151 | |||
152 | public function getRemoveAllFacetsUri(SearchRequest $previousSearchRequest): string |
||
153 | { |
||
154 | $persistentAndFacetArguments = $previousSearchRequest |
||
155 | ->getCopyForSubRequest()->removeAllGroupItemPages()->removeAllFacets() |
||
156 | ->getAsArray(); |
||
157 | |||
158 | $additionalArguments = []; |
||
159 | if ( |
||
160 | ($typoScriptConfiguration = $previousSearchRequest->getContextTypoScriptConfiguration()) |
||
161 | && $typoScriptConfiguration instanceof TypoScriptConfiguration |
||
162 | && $typoScriptConfiguration->getSearchFacetingFacetLinkUrlParametersUseForFacetResetLinkUrl() |
||
163 | ) { |
||
164 | $additionalArguments = $this->getAdditionalArgumentsFromRequestConfiguration($previousSearchRequest); |
||
165 | } |
||
166 | |||
167 | $arguments = $persistentAndFacetArguments + $additionalArguments; |
||
168 | |||
169 | $this->sortFilterParametersIfNecessary($previousSearchRequest, $arguments); |
||
170 | |||
171 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
172 | return $this->buildLinkWithInMemoryCache($pageUid, $arguments); |
||
173 | } |
||
174 | |||
175 | public function getResultPageUri(SearchRequest $previousSearchRequest, $page): string |
||
176 | { |
||
177 | $persistentAndFacetArguments = $previousSearchRequest |
||
178 | ->getCopyForSubRequest()->setPage($page) |
||
179 | ->getAsArray(); |
||
180 | |||
181 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
182 | return $this->buildLinkWithInMemoryCache($pageUid, $persistentAndFacetArguments); |
||
183 | } |
||
184 | |||
185 | public function getResultGroupItemPageUri(SearchRequest $previousSearchRequest, GroupItem $groupItem, int $page): string |
||
186 | { |
||
187 | $persistentAndFacetArguments = $previousSearchRequest |
||
188 | ->getCopyForSubRequest()->setGroupItemPage($groupItem->getGroup()->getGroupName(), $groupItem->getGroupValue(), $page) |
||
189 | ->getAsArray(); |
||
190 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
191 | return $this->buildLinkWithInMemoryCache($pageUid, $persistentAndFacetArguments); |
||
192 | } |
||
193 | |||
194 | public function getNewSearchUri(SearchRequest $previousSearchRequest, $queryString): string |
||
195 | { |
||
196 | /** @var $request SearchRequest */ |
||
197 | $contextConfiguration = $previousSearchRequest->getContextTypoScriptConfiguration(); |
||
198 | $contextSystemLanguage = $previousSearchRequest->getContextSystemLanguageUid(); |
||
199 | $contextPageUid = $previousSearchRequest->getContextPageUid(); |
||
200 | |||
201 | $request = GeneralUtility::makeInstance( |
||
202 | SearchRequest::class, |
||
203 | [], |
||
204 | /** @scrutinizer ignore-type */ |
||
205 | $contextPageUid, |
||
206 | /** @scrutinizer ignore-type */ |
||
207 | $contextSystemLanguage, |
||
208 | /** @scrutinizer ignore-type */ |
||
209 | $contextConfiguration |
||
210 | ); |
||
211 | $arguments = $request->setRawQueryString($queryString)->getAsArray(); |
||
212 | |||
213 | $this->sortFilterParametersIfNecessary($previousSearchRequest, $arguments); |
||
214 | |||
215 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
216 | return $this->buildLinkWithInMemoryCache($pageUid, $arguments); |
||
217 | } |
||
218 | |||
219 | public function getSetSortingUri(SearchRequest $previousSearchRequest, $sortingName, $sortingDirection): string |
||
220 | { |
||
221 | $persistentAndFacetArguments = $previousSearchRequest |
||
222 | ->getCopyForSubRequest()->setSorting($sortingName, $sortingDirection) |
||
223 | ->getAsArray(); |
||
224 | |||
225 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
226 | return $this->buildLinkWithInMemoryCache($pageUid, $persistentAndFacetArguments); |
||
227 | } |
||
228 | |||
229 | public function getRemoveSortingUri(SearchRequest $previousSearchRequest): string |
||
230 | { |
||
231 | $persistentAndFacetArguments = $previousSearchRequest |
||
232 | ->getCopyForSubRequest()->removeSorting() |
||
233 | ->getAsArray(); |
||
234 | |||
235 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
236 | return $this->buildLinkWithInMemoryCache($pageUid, $persistentAndFacetArguments); |
||
237 | } |
||
238 | |||
239 | public function getCurrentSearchUri(SearchRequest $previousSearchRequest): string |
||
240 | { |
||
241 | $persistentAndFacetArguments = $previousSearchRequest |
||
242 | ->getCopyForSubRequest() |
||
243 | ->getAsArray(); |
||
244 | |||
245 | $pageUid = $this->getTargetPageUidFromRequestConfiguration($previousSearchRequest); |
||
246 | return $this->buildLinkWithInMemoryCache($pageUid, $persistentAndFacetArguments); |
||
247 | } |
||
248 | |||
249 | protected function getAdditionalArgumentsFromRequestConfiguration(SearchRequest $request): array |
||
250 | { |
||
251 | if ($request->getContextTypoScriptConfiguration() === null) { |
||
252 | return []; |
||
253 | } |
||
254 | |||
255 | $reQuestId = $request->getId(); |
||
256 | if (isset(self::$additionalArgumentsCache[$reQuestId])) { |
||
257 | return self::$additionalArgumentsCache[$reQuestId]; |
||
258 | } |
||
259 | |||
260 | self::$additionalArgumentsCache[$reQuestId] = $request->getContextTypoScriptConfiguration() |
||
261 | ->getSearchFacetingFacetLinkUrlParametersAsArray(); |
||
262 | |||
263 | return self::$additionalArgumentsCache[$reQuestId]; |
||
264 | } |
||
265 | |||
266 | protected function getTargetPageUidFromRequestConfiguration(SearchRequest $request): ?int |
||
267 | { |
||
268 | return $request->getContextTypoScriptConfiguration()?->getSearchTargetPage(); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Build the link with an i memory cache that reduces the amount of required typolink calls. |
||
273 | */ |
||
274 | protected function buildLinkWithInMemoryCache(?int $pageUid, array $arguments): string |
||
275 | { |
||
276 | $values = []; |
||
277 | $structure = $arguments; |
||
278 | $this->getSubstitution($structure, $values); |
||
279 | $hash = md5($pageUid . json_encode($structure)); |
||
280 | if (isset(self::$preCompiledLinks[$hash])) { |
||
281 | self::$hitCount++; |
||
282 | $uriCacheTemplate = self::$preCompiledLinks[$hash]; |
||
283 | } else { |
||
284 | self::$missCount++; |
||
285 | $this->uriBuilder->reset()->setTargetPageUid($pageUid); |
||
286 | $uriCacheTemplate = $this->uriBuilder->setArguments($structure)->build(); |
||
287 | |||
288 | /* @var UrlHelper $urlHelper */ |
||
289 | $urlHelper = GeneralUtility::makeInstance(UrlHelper::class, $uriCacheTemplate); |
||
290 | self::$preCompiledLinks[$hash] = (string)$urlHelper; |
||
291 | } |
||
292 | |||
293 | $keys = array_map(static function ($value) { |
||
294 | return urlencode((string)$value); |
||
295 | }, array_keys($values)); |
||
296 | $values = array_map(static function ($value) { |
||
297 | return urlencode((string)$value); |
||
298 | }, $values); |
||
299 | |||
300 | $routingConfigurations = $this->routingService |
||
301 | ->fetchEnhancerByPageUid($pageUid); |
||
302 | $enhancedRouting = count($routingConfigurations) > 0; |
||
303 | $this->routingService->reset(); |
||
304 | if ($enhancedRouting && is_array($routingConfigurations[0] ?? null)) { |
||
305 | $this->routingService->fromRoutingConfiguration($routingConfigurations[0]); |
||
306 | } |
||
307 | |||
308 | /* @var Uri $uri */ |
||
309 | $uri = GeneralUtility::makeInstance( |
||
310 | Uri::class, |
||
311 | $uriCacheTemplate |
||
312 | ); |
||
313 | |||
314 | $urlEvent = new BeforeReplaceVariableInCachedUrlEvent($uri, $enhancedRouting); |
||
315 | /* @var BeforeReplaceVariableInCachedUrlEvent $urlEvent */ |
||
316 | $urlEvent = $this->eventDispatcher->dispatch($urlEvent); |
||
317 | $uriCacheTemplate = (string)$urlEvent->getUri(); |
||
318 | |||
319 | $variableEvent = new BeforeProcessCachedVariablesEvent( |
||
320 | $uri, |
||
321 | $routingConfigurations, |
||
322 | $keys, |
||
323 | $values |
||
324 | ); |
||
325 | $this->eventDispatcher->dispatch($variableEvent); |
||
326 | |||
327 | $values = $variableEvent->getVariableValues(); |
||
328 | // Take care that everything is urlencoded! |
||
329 | $keys = array_map(static function ($value) { |
||
330 | if (!str_contains($value, '###')) { |
||
331 | return $value; |
||
332 | } |
||
333 | return urlencode($value); |
||
334 | }, array_keys($values)); |
||
335 | |||
336 | $uri = str_replace($keys, $values, $uriCacheTemplate); |
||
337 | $uri = GeneralUtility::makeInstance( |
||
338 | Uri::class, |
||
339 | $uri |
||
340 | ); |
||
341 | $uriEvent = new PostProcessUriEvent($uri, $routingConfigurations); |
||
342 | $this->eventDispatcher->dispatch($uriEvent); |
||
343 | $uri = $uriEvent->getUri(); |
||
344 | return (string)$uri; |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Flushes the internal in memory cache. |
||
349 | */ |
||
350 | public function flushInMemoryCache(): void |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * This method is used to build two arrays from a nested array. The first one represents the structure. |
||
357 | * In this structure the values are replaced with the pass to the value. At the same time the values get collected |
||
358 | * in the $values array, with the path as key. This can be used to build a comparable hash from the arguments |
||
359 | * in order to reduce the amount of typolink calls |
||
360 | * |
||
361 | * Example input |
||
362 | * |
||
363 | * $data = [ |
||
364 | * 'foo' => [ |
||
365 | * 'bar' => 111 |
||
366 | * ] |
||
367 | * ] |
||
368 | * |
||
369 | * will return: |
||
370 | * |
||
371 | * $structure = [ |
||
372 | * 'foo' => [ |
||
373 | * 'bar' => '###foo:bar###' |
||
374 | * ] |
||
375 | * ] |
||
376 | * |
||
377 | * $values = [ |
||
378 | * '###foo:bar###' => 111 |
||
379 | * ] |
||
380 | */ |
||
381 | protected function getSubstitution(array &$structure, array &$values, array $branch = []): void |
||
382 | { |
||
383 | /* |
||
384 | * Adds information about the filter facet to the placeholder. |
||
385 | * |
||
386 | * This feature allows the handle even placeholder in RouteEnhancer |
||
387 | */ |
||
388 | $filter = false; |
||
389 | if (count($branch) > 0 && $branch[count($branch) - 1] === 'filter') { |
||
390 | $filter = true; |
||
391 | } |
||
392 | foreach ($structure as $key => &$value) { |
||
393 | $branch[] = $key; |
||
394 | if (is_array($value)) { |
||
395 | $this->getSubstitution($value, $values, $branch); |
||
396 | } else { |
||
397 | // @todo: Refactor to multi-dimensional array. |
||
398 | // https://solr-ddev-site.ddev.site/content-examples/form-elements/search?tx_solr[filter][type:tx_news_domain_model_news]=1&tx_solr[q]=* |
||
399 | // https://solr-ddev-site.ddev.site/content-examples/form-elements/search?tx_solr[filter][0]=type:pages&tx_solr[q]=* |
||
400 | if ($filter && $value !== 1) { |
||
401 | [$facetType] = explode(':', $value); |
||
402 | $branch[] = $facetType; |
||
403 | } |
||
404 | $path = '###' . implode(':', $branch) . '###'; |
||
405 | $values[$path] = $value; |
||
406 | $structure[$key] = $path; |
||
407 | if ($filter) { |
||
408 | array_pop($branch); |
||
409 | } |
||
410 | } |
||
411 | array_pop($branch); |
||
412 | } |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Sorts filter arguments if enabled. |
||
417 | */ |
||
418 | protected function sortFilterParametersIfNecessary(SearchRequest $searchRequest, array &$arguments): void |
||
433 | ); |
||
434 | } |
||
435 | } |
||
436 | } |
||
437 | } |
||
438 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths