Files
kupshop/bundles/KupShop/I18nBundle/Util/Locale/LanguageAwareUrlGenerator.php
2025-08-02 16:30:27 +02:00

127 lines
4.3 KiB
PHP

<?php
namespace KupShop\I18nBundle\Util\Locale;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\HTTPUtil;
use KupShop\KupShopBundle\Util\System\ControllerUtil;
use KupShop\KupShopBundle\Views\RouteAwareResponderInterface;
use KupShop\LocalePrefixBundle\Util\LocalePrefixUtil;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
class LanguageAwareUrlGenerator
{
public function __construct(
private readonly RouterInterface $router,
private readonly LanguageContext $languageContext,
private readonly ControllerUtil $controllerUtil,
private readonly ContextManager $contextManager,
private readonly RequestStack $requestStack,
private readonly ?LocalePrefixUtil $localePrefixUtil = null,
) {
}
public function generateInLanguage(string $language, string $path = '/'): string
{
$request = $this->createRequest($path);
return $this->withMainRequest($request, function () use ($request, $language) {
$fallback = true;
if ($match = $this->controllerUtil->getControllerByRequest($request)) {
if ($url['path'] = $this->getCorrectUrlFromController($request, $match, $language)) {
return HTTPUtil::http_build_url($url);
}
$fallback = false;
try {
$url['path'] = $pathInLanguage = $this->router->generateInLanguage($language, $match['_route'], $match['_route_params']);
$url = HTTPUtil::http_build_url($url);
if (!$url || empty($pathInLanguage)) {
$fallback = true;
}
} catch (\Exception) {
$fallback = true;
}
}
if ($fallback) {
$url = $this->router->generateInLanguage($language, 'home');
}
$this->languageContext->activate($language);
return $url;
});
}
private function getCorrectUrlFromController(Request $request, array $match, string $language): ?string
{
$controller = $this->controllerUtil->instantiateController($match['_controller']);
$controllerReturn = clone $this->controllerUtil->callController($request, $controller);
if (!($controllerReturn instanceof RouteAwareResponderInterface)) {
return null;
}
try {
$url = $this->contextManager->activateContexts(
[LanguageContext::class => $language],
fn (): ?string => $controllerReturn->getCorrectUrl(),
);
} catch (NotFoundHttpException) {
$url = null;
}
return $url === null ? $this->router->generateInLanguage($language, 'home') : $url;
}
private function createRequest(string $uri): Request
{
$request = Request::create($uri);
$request->attributes->set('path', ltrim(parse_url($uri)['path'] ?? '/', '/'));
if (findModule(\Modules::LOCALE_PREFIX)) {
$this->localePrefixUtil->setRequestAttribute($request);
}
if ($this->requestStack->getMainRequest()->hasSession()) {
$request->setSession(
$this->requestStack->getMainRequest()->getSession()
);
}
return $request;
}
private function withMainRequest(Request $request, callable $fn): mixed
{
$prevRequests = [];
while ($this->requestStack->getCurrentRequest()) {
// no-op: intentionally discarding all requests so request in args will be main request
$prevRequests[] = $this->requestStack->pop();
}
// reverse, so last pop request is first
$prevRequests = array_reverse($prevRequests);
// push arg request to requests so it becomes main request
$this->requestStack->push($request);
$result = $fn();
// remove arg request
$this->requestStack->pop();
// restore prev requests
foreach ($prevRequests as $prevRequest) {
$this->requestStack->push($prevRequest);
}
return $result;
}
}