Skip to content

Commit

Permalink
[BUGFIX] Retry Uri Building after exception
Browse files Browse the repository at this point in the history
When there are route enhancers defined for a page containing
the Solr search plugin, e.g. to realize pretty URLs for the
search term, filters or pagination, there are issues with
the URI caching that utilizes placeholders.

Route Enhancers should specify the expected format using the
requirements setting of the route enhancer parameters. This
will then lead to an exception, which is caught and will then
proceed the URI building with the original arguments instead
of the placeholder arguments.

Resolves: #2984
  • Loading branch information
saitho authored and dkd-friedrich committed Sep 20, 2023
1 parent a4013bd commit ba1de1c
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 5 deletions.
26 changes: 21 additions & 5 deletions Classes/Domain/Search/Uri/SearchUriBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
67 changes: 67 additions & 0 deletions Tests/Unit/Domain/Search/Uri/SearchUriBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

0 comments on commit ba1de1c

Please sign in to comment.