Skip to content

Commit

Permalink
[TASK] Remove deprecated instance usage and improve configuration and…
Browse files Browse the repository at this point in the history
… repositories
  • Loading branch information
garbast committed Nov 16, 2024
1 parent 29c06d2 commit 8bc6483
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 175 deletions.
214 changes: 99 additions & 115 deletions Classes/Domain/Repository/LocationRepository.php

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ModifyMiddlewareLocationsListener
{
public function __construct(private ContentObjectRenderer $contentObjectRenderer) {}

// #[AsEventListener('storefinder_middleware_locationsfetched', ModifyMiddlewareLocationsEvent::class)]
#[AsEventListener('storefinder_middleware_locationsfetched', ModifyMiddlewareLocationsEvent::class)]
public function __invoke(ModifyMiddlewareLocationsEvent $event): void
{
/* do what ever you need to change */
Expand Down
244 changes: 195 additions & 49 deletions Classes/Middleware/StoreFinderMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

namespace Evoweb\StoreFinder\Middleware;

use Doctrine\DBAL\Exception;
use Evoweb\StoreFinder\Domain\Model\Constraint;
use Evoweb\StoreFinder\Domain\Repository\CategoryRepository;
use Evoweb\StoreFinder\Domain\Repository\ContentRepository;
use Evoweb\StoreFinder\Domain\Repository\CountryRepository;
use Evoweb\StoreFinder\Domain\Repository\LocationRepository;
use Evoweb\StoreFinder\Middleware\Event\ModifyMiddlewareCategoriesEvent;
use Evoweb\StoreFinder\Middleware\Event\ModifyMiddlewareLocationsEvent;
Expand All @@ -28,80 +28,214 @@
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use SJBR\StaticInfoTables\Domain\Model\CountryZone;
use SJBR\StaticInfoTables\Domain\Repository\CountryZoneRepository;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\Attribute\Lazy;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
use TYPO3\CMS\Core\Country\CountryProvider;
use TYPO3\CMS\Core\Error\Http\StatusException;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\TypoScript\FrontendTypoScript;
use TYPO3\CMS\Core\TypoScript\FrontendTypoScriptFactory;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Page\PageInformation;
use TYPO3\CMS\Frontend\Page\PageInformationCreationFailedException;
use TYPO3\CMS\Frontend\Page\PageInformationFactory;

class StoreFinderMiddleware implements MiddlewareInterface
final readonly class StoreFinderMiddleware implements MiddlewareInterface
{
public function __construct(
protected EventDispatcherInterface $eventDispatcher,
protected CacheManager $cacheManager
#[Lazy]
private EventDispatcherInterface $eventDispatcher,
#[Lazy]
private CacheManager $cacheManager,
#[Lazy]
private ContentRepository $contentRepository,
#[Lazy]
private FlexFormService $flexFormService,
#[Lazy]
private TypoScriptService $typoScriptService,

private FrontendTypoScriptFactory $frontendTypoScriptFactory,
private PageInformationFactory $pageInformationFactory,
#[Autowire(service: 'cache.typoscript')]
private PhpFrontend $typoScriptCache,
) {}

/**
* @throws PageInformationCreationFailedException
* @throws StatusException
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$path = ltrim($request->getUri()->getPath(), '/');
$queryParams = $request->getQueryParams();
$action = $queryParams['action'] ?? '';
if (!str_contains($path, 'api/storefinder/') || !in_array($action, ['locations', 'categories'])) {
if (!$this->processMiddleware($request)) {
return $handler->handle($request);
}

$request->getBody()->rewind();
$json = $request->getBody()->getContents();
/** @var SiteLanguage $requestLanguage */
$requestLanguage = $request->getAttribute('language');
$contentUid = $queryParams['contentUid'] ?? 0;
// @extensionScannerIgnoreLine
$cacheIdentifier = md5('store_finder' . $action . $contentUid . $requestLanguage->getLanguageId());
$locale = (string)$requestLanguage->getLocale();
$action = $request->getQueryParams()['action'] ?? '';
$contentUid = (int)($request->getQueryParams()['contentUid'] ?? 0);

$cache = $this->cacheManager->getCache('store_finder_middleware_cache');
if (empty($request->getBody()->getContents()) && $cache->has($cacheIdentifier)) {
$cacheIdentifier = $this->getCacheIdentifier($json, $locale, $action, $contentUid);
$cache = $this->getCache();
if ($cache && $cache->has($cacheIdentifier)) {
$rows = $cache->get($cacheIdentifier);
} else {
[$settings, $request] = $this->getSettings($request, (int)$contentUid);
[$settings, $request] = $this->getSettings($request, $contentUid);
$rows = $this->{$action . 'Action'}($request, $settings);
$request->getBody()->rewind();
if (empty($request->getBody()->getContents())) {
$cache->set($cacheIdentifier, $rows);
}
$cache?->set($cacheIdentifier, $rows);
}

return new JsonResponse($rows);
}

protected function getCache(): ?FrontendInterface
{
try {
$cache = $this->cacheManager->getCache('store_finder_middleware_cache') ?? null;
} catch (NoSuchCacheException) {
$cache = null;
}
return $cache;
}

protected function processMiddleware(ServerRequestInterface $request): bool
{
return str_contains($request->getUri()->getPath(), 'api/storefinder/')
&& in_array(($request->getQueryParams()['action'] ?? ''), ['locations', 'categories']);
}

protected function getCacheIdentifier(string $json, string $locale, string $action, int $contentUid): string
{
$encryptionKey = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] ?? '';
return sha1($encryptionKey . 'store_finder' . $json . $locale . $action . $contentUid);
}

/**
* @return array<array<string, mixed>|ServerRequestInterface>
* @throws PageInformationCreationFailedException
* @throws StatusException
*/
protected function getSettings(ServerRequestInterface $request, int $contentUid): array
{
/** @var ContentRepository $contentRepository */
$contentRepository = GeneralUtility::makeInstance(ContentRepository::class);
/** @var FlexFormService $flexFormService */
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
/** @var TypoScriptService $typoScriptService */
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);

$row = $contentRepository->findByUid($contentUid);
$settings = $flexFormService->convertFlexFormContentToArray($row['pi_flexform'] ?? '')['settings'] ?? [];
$pageId = $request->getAttribute('routing')->getPageId();
$contentSettings = $this->getContentSettings($contentUid);

if ($pageId === $contentSettings['pid']) {
$typoScript = $request->getAttribute('frontend.typoscript');
} else {
$pageInformation = $this->getPageInformation($request, $contentSettings['pid']);
$typoScript = $this->getTypoScriptForPage($request, $pageInformation);
}

$settings = $this->typoScriptService->convertTypoScriptArrayToPlainArray(
$typoScript->getSetupArray()['plugin.']['tx_storefinder.']['ajax.'] ?? [],
) + $contentSettings;

return [$settings, $request];
}

/**
* @throws PageInformationCreationFailedException
* @throws StatusException
*/
protected function getPageInformation(ServerRequestInterface $request, int $pageUid): PageInformation
{
$request = $request->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE);
$request = $request->withAttribute('routing', new PageArguments($pageUid, '0', []));
return $this->pageInformationFactory->create($request);
}

/**
* @return array<string, mixed>
*/
protected function getContentSettings(int $contentUid): array
{
$row = $this->contentRepository->findByUid($contentUid);

$settings = $this->flexFormService->convertFlexFormContentToArray($row['pi_flexform'] ?? '')['settings'] ?? [];
$settings['pid'] = $row['pid'];
$settings['storagePid'] = $row['pages'];

$controller = $request->getAttribute('frontend.controller');
// @extensionScannerIgnoreLine
$controller->id = $row['pid'];
// @extensionScannerIgnoreLine
$controller->determineId($request);
$request = $controller->getFromCache($request);
return $settings;
}

protected function getTypoScriptForPage(
ServerRequestInterface $request,
PageInformation $pageInformation,
): FrontendTypoScript {
$site = $request->getAttribute('site');
$pageType = $request->getAttribute('routing')->getPageType();
$sysTemplateRows = $pageInformation->getSysTemplateRows();
$isCachingAllowed = $request->getAttribute('frontend.cache.instruction')->isCachingAllowed();
$conditionMatcherVariables = $this->prepareConditionMatcherVariables($request, $pageInformation);

$typoScript = $request->getAttribute('frontend.typoscript');
$settings += $typoScriptService->convertTypoScriptArrayToPlainArray(
$typoScript->getSetupArray()['plugin.']['tx_storefinder.']['ajax.'] ?? []
$frontendTypoScript = $this->frontendTypoScriptFactory->createSettingsAndSetupConditions(
$site,
$sysTemplateRows,
$conditionMatcherVariables,
$isCachingAllowed ? $this->typoScriptCache : null,
);

return [$settings, $request];
try {
$frontendTypoScript = $this->frontendTypoScriptFactory->createSetupConfigOrFullSetup(
true,
$frontendTypoScript,
$site,
$sysTemplateRows,
$conditionMatcherVariables,
$pageType,
$isCachingAllowed ? $this->typoScriptCache : null,
$request,
);
} catch (\JsonException) {
}

return $frontendTypoScript;
}

/**
* @return array<string, mixed>
*/
private function prepareConditionMatcherVariables(
ServerRequestInterface $request,
PageInformation $pageInformation,
): array {
$topDownRootLine = $pageInformation->getRootLine();
$localRootline = $pageInformation->getLocalRootLine();
ksort($topDownRootLine);
return [
'request' => $request,
'pageId' => $pageInformation->getId(),
'page' => $pageInformation->getPageRecord(),
'fullRootLine' => $topDownRootLine,
'localRootLine' => $localRootline,
'site' => $request->getAttribute('site'),
'siteLanguage' => $request->getAttribute('language'),
'tsfe' => $request->getAttribute('frontend.controller'),
];
}

/**
* @param array<string, mixed> $settings
* @return array<array<string, mixed>>
* @throws Exception
* @throws AspectNotFoundException
*/
protected function categoriesAction(ServerRequestInterface $request, array $settings): array
{
/** @var CategoryRepository $categoryRepository */
Expand All @@ -112,44 +246,55 @@ protected function categoriesAction(ServerRequestInterface $request, array $sett
$categoryTree = $categoryRepository->getCategoriesByParents($categories);

$eventResult = $this->eventDispatcher->dispatch(
new ModifyMiddlewareCategoriesEvent($request, $this, $settings, $categoryTree)
new ModifyMiddlewareCategoriesEvent($request, $this, $settings, $categoryTree),
);
return $eventResult->getCategories();
}

/**
* @param array<string, mixed> $settings
* @return array<array<string, mixed>>
* @throws Exception
* @throws AspectNotFoundException
*/
protected function locationsAction(ServerRequestInterface $request, array $settings): array
{
/** @var LocationRepository $locationRepository */
$locationRepository = GeneralUtility::makeInstance(LocationRepository::class);
$locationRepository->setSettings($settings);

$constraint = $this->prepareConstraint($request, $settings);
$rows = $locationRepository->getLocations($constraint);
$json = $request->getBody()->getContents();
$constraint = $this->prepareConstraint($json, $settings);
$rows = $locationRepository->findAllForAjaxMiddleware($constraint);

$eventResult = $this->eventDispatcher->dispatch(
new ModifyMiddlewareLocationsEvent($request, $this, $settings, $rows)
new ModifyMiddlewareLocationsEvent($request, $this, $settings, $rows),
);
return $eventResult->getLocations();
}

protected function prepareConstraint(ServerRequestInterface $request, array $settings): Constraint
/**
* @param array<string, mixed> $settings
* @throws Exception
*/
protected function prepareConstraint(string $json, array $settings): Constraint
{
/** @var Constraint $constraint */
$constraint = GeneralUtility::makeInstance(Constraint::class);
$request->getBody()->rewind();
$post = json_decode($request->getBody()->getContents(), true);
$post = strlen($json) > 0 ? json_decode($json, true) : [];

if (!empty($post['address'])) {
if ((int)$settings['country'] ?? false) {
/** @var CountryRepository $countryRepository */
$countryRepository = GeneralUtility::makeInstance(CountryRepository::class);
$country = $countryRepository->findByUid((int)$settings['country']);
if ((string)($settings['country'] ?? '')) {
/** @var CountryProvider $countryProvider */
$countryProvider = GeneralUtility::makeInstance(CountryProvider::class);
$country = $countryProvider->getByAlpha2IsoCode((string)$settings['country']);
$constraint->setCountry($country);
}

if ((int)$settings['state'] ?? false) {
if ((int)($settings['state'] ?? 0)) {
/** @var CountryZoneRepository $countryZoneRepository */
$countryZoneRepository = GeneralUtility::makeInstance(CountryZoneRepository::class);
/** @var CountryZone $countryZone */
$countryZone = $countryZoneRepository->findByUid((int)$settings['state']);
$constraint->setState($countryZone);
}
Expand All @@ -176,6 +321,7 @@ protected function prepareConstraint(ServerRequestInterface $request, array $set
/** @var GeocodeService $geocodeService */
$geocodeService = GeneralUtility::makeInstance(GeocodeService::class);
$geocodeService->setSettings($settings);
/** @var Constraint $constraint */
$constraint = $geocodeService->geocodeAddress($constraint);
}

Expand Down
1 change: 0 additions & 1 deletion Configuration/Routes/Ajax.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ routeEnhancers:
requirements:
action: '(categories|locations)'
contentUid: '[0-9].*'
language: '.*'
_arguments:
action: 'action'
content: 'contentUid'
11 changes: 10 additions & 1 deletion Configuration/Sets/StoreFinder/labels.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
<trans-unit id="settings.description.evoweb.store-finder.layoutRootPaths" resname="settings.description.evoweb.store-finder.layoutRootPaths">
<source/>
</trans-unit>
<trans-unit id="settings.description.evoweb.store-finder.categories" resname="settings.description.evoweb.store-finder.categories">
<source>List of parent category</source>
</trans-unit>
<trans-unit id="settings.evoweb.store-finder.ajax.templateRootPaths" resname="settings.evoweb.store-finder.ajax.templateRootPaths">
<source>Template root path for ajax rendering</source>
</trans-unit>
Expand All @@ -99,7 +102,13 @@
<source>Comma separated list of page IDs of folder in which the locations are stored</source>
</trans-unit>
<trans-unit id="settings.description.evoweb.store-finder.ajax.storagePid" resname="settings.description.evoweb.store-finder.ajax.storagePid">
<source/>
<source>List of storage pid</source>
</trans-unit>
<trans-unit id="settings.evoweb.store-finder.ajax.categories" resname="settings.evoweb.store-finder.ajax.categories">
<source>Comma separated list of parent categories</source>
</trans-unit>
<trans-unit id="settings.description.evoweb.store-finder.ajax.categories" resname="settings.description.evoweb.store-finder.ajax.categories">
<source>List of parent categories</source>
</trans-unit>
</body>
</file>
Expand Down
8 changes: 8 additions & 0 deletions Configuration/Sets/StoreFinder/settings.definitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ settings:
default: 0
type: bool
category: store-finder.common
evoweb.store-finder.categories:
default: ''
type: string
category: store-finder.common
evoweb.store-finder.templateRootPaths:
default: 'EXT:store_finder/Resources/Private/Templates/'
type: string
Expand Down Expand Up @@ -64,3 +68,7 @@ settings:
default: ''
type: string
category: store-finder.ajax
evoweb.store-finder.ajax.categories:
default: ''
type: string
category: store-finder.ajax
Loading

0 comments on commit 8bc6483

Please sign in to comment.