diff --git a/Classes/Domain/Search/Uri/SearchUriBuilder.php b/Classes/Domain/Search/Uri/SearchUriBuilder.php index e42771888d..b5e91cf1c4 100644 --- a/Classes/Domain/Search/Uri/SearchUriBuilder.php +++ b/Classes/Domain/Search/Uri/SearchUriBuilder.php @@ -27,6 +27,7 @@ use ApacheSolrForTypo3\Solr\System\Url\UrlHelper; use ApacheSolrForTypo3\Solr\Utility\ParameterSortingUtility; use Psr\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Exception\InvalidParameterException; use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; @@ -277,11 +278,26 @@ protected function buildLinkWithInMemoryCache(?int $pageUid, array $arguments): } else { self::$missCount++; $this->uriBuilder->reset()->setTargetPageUid($pageUid); - $uriCacheTemplate = $this->uriBuilder->setArguments($structure)->build(); - - /** @var UrlHelper $urlHelper */ - $urlHelper = GeneralUtility::makeInstance(UrlHelper::class, $uriCacheTemplate); - self::$preCompiledLinks[$hash] = (string)$urlHelper; + try { + $uriCacheTemplate = $this->uriBuilder->setArguments($structure)->build(); + + /** @var UrlHelper $urlHelper */ + $urlHelper = GeneralUtility::makeInstance(UrlHelper::class, $uriCacheTemplate); + self::$preCompiledLinks[$hash] = (string)$urlHelper; + } catch (InvalidParameterException $exception) { + // the placeholders may result in an exception when route enhancers with requirements are active + // In this case, try to build the URL with original arguments + $hash = md5($pageUid . json_encode($arguments)); + if (isset(self::$preCompiledLinks[$hash])) { + self::$hitCount++; + $uriCacheTemplate = self::$preCompiledLinks[$hash]; + } else { + $uriCacheTemplate = $this->uriBuilder->setArguments($arguments)->build(); + /** @var UrlHelper $urlHelper */ + $urlHelper = GeneralUtility::makeInstance(UrlHelper::class, $uriCacheTemplate); + self::$preCompiledLinks[$hash] = (string)$urlHelper; + } + } } $keys = array_map(static function ($value) { diff --git a/Tests/Unit/Domain/Search/Uri/SearchUriBuilderTest.php b/Tests/Unit/Domain/Search/Uri/SearchUriBuilderTest.php index 223b44bcd7..69d2197162 100644 --- a/Tests/Unit/Domain/Search/Uri/SearchUriBuilderTest.php +++ b/Tests/Unit/Domain/Search/Uri/SearchUriBuilderTest.php @@ -23,6 +23,7 @@ use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\Tests\Unit\SetUpUnitTestCase; use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Routing\Exception\InvalidParameterException; use Symfony\Component\Yaml\Yaml; use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; @@ -469,4 +470,70 @@ public function siteConfigurationModifyUriKeepUnmappedFilterTest(): void $uri = $this->searchUrlBuilder->getResultPageUri($previousRequest, 0); self::assertEquals($linkBuilderResult, $uri); } + + /** + * @test + */ + public function uriErrorsResultInNonMappedProcessing(): void + { + $configuration = Yaml::parse($this->getFixtureContentByName('siteConfiguration.yaml')); + $routingServiceMock = $this->createMock(RoutingService::class); + $routingServiceMock->expects(self::any()) + ->method('fetchEnhancerByPageUid') + ->willReturn($configuration['routeEnhancers']['example']); + $queryParameters = [ + 'tx_solr' => [ + 'filter' => [ + 'type:pages', + 'color:green', + 'color:red', + 'color:yellow', + 'taste:matcha', + 'taste:sour', + 'product:candy', + 'product:sweets', + 'quantity:20', + ], + ], + ]; + $subsitutedQueryParameters = [ + 'tx_solr' => [ + 'filter' => [ + '###tx_solr:filter:0:type###', + '###tx_solr:filter:1:color###', + '###tx_solr:filter:2:color###', + '###tx_solr:filter:3:color###', + '###tx_solr:filter:4:taste###', + '###tx_solr:filter:5:taste###', + '###tx_solr:filter:6:product###', + '###tx_solr:filter:7:product###', + '###tx_solr:filter:8:quantity###', + ], + ], + ]; + $linkBuilderResult = '/index.php?id=42&color=' . urlencode('green,red,yellow') . + '&taste=' . urlencode('matcha,sour') . + '&product=' . urlencode('candy,sweets') . + '&' . urlencode('tx_solr[filter][0]') . '=' . urlencode('quantity:20'); + $configurationMock = $this->createMock(TypoScriptConfiguration::class); + $configurationMock->expects(self::any())->method('getSearchPluginNamespace')->willReturn('tx_solr'); + $configurationMock->expects(self::once())->method('getSearchTargetPage')->willReturn(42); + + $previousRequest = new SearchRequest($queryParameters, 42, 0, $configurationMock); + $this->extBaseUriBuilderMock->expects(self::any())->method('setArguments') + ->withConsecutive([$subsitutedQueryParameters], [$queryParameters]) + ->willReturn($this->extBaseUriBuilderMock); + $this->extBaseUriBuilderMock->expects(self::once())->method('reset')->with()->willReturn($this->extBaseUriBuilderMock); + $buildCounter = 0; + $this->extBaseUriBuilderMock->expects(self::exactly(2))->method('build') + ->willReturnCallback(function () use ($linkBuilderResult, &$buildCounter) { + if (++$buildCounter === 1) { + throw new InvalidParameterException('First call fails, should reprocess with regular arguments'); + } + return $linkBuilderResult; + }); + $this->searchUrlBuilder->injectRoutingService($routingServiceMock); + $uri = $this->searchUrlBuilder->getResultPageUri($previousRequest, 0); + self::assertEquals($linkBuilderResult, $uri); + } }