<?php
declare(strict_types=1);
namespace App\Routing;
use App\Locale\LocaleProviderInterface;
use App\Locale\LocaleResolver;
use BestIt\Routing\Router\AbstractRouter;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;
/**
* Class I18nRouter
*
* @author André Varelmann <andre.varelmann@bestit-online.de>
* @package App\Routing
*/
class I18nRouter extends AbstractRouter
{
private RouterInterface $defaultRouter;
private I18nLoader $i18nLoader;
private LocaleResolver $localeResolver;
private LocaleProviderInterface $localeProvider;
private ?RouteCollection $routeCollection = null;
private ?UrlGeneratorInterface $generator = null;
/**
* I18nRouter constructor.
*
* @param RouterInterface $defaultRouter
* @param I18nLoader $i18nLoader
* @param LocaleResolver $localeResolver
* @param LocaleProviderInterface $localeProvider
*/
public function __construct(
RouterInterface $defaultRouter,
I18nLoader $i18nLoader,
LocaleResolver $localeResolver,
LocaleProviderInterface $localeProvider
) {
$this->defaultRouter = $defaultRouter;
$this->localeResolver = $localeResolver;
$this->i18nLoader = $i18nLoader;
$this->localeProvider = $localeProvider;
}
/**
* @return RouteCollection
*/
public function getRouteCollection(): RouteCollection
{
if (!$this->routeCollection) {
$this->routeCollection = $this->i18nLoader->load($this->defaultRouter->getRouteCollection());
}
return $this->routeCollection;
}
public function getGenerator(): UrlGeneratorInterface
{
if (null === $this->generator) {
$this->generator = new UrlGenerator(
$this->getRouteCollection(),
$this->defaultRouter->getContext(),
null,
$this->localeProvider->getLocaleInfo()->getDefaultLocale()
);
}
return $this->generator;
}
/**
* Generates a URL or path for a specific route based on the given parameters.
*
* @throws RouteNotFoundException
*
* @param string $name
* @param array $parameters
* @param int $referenceType
*
* @return string
*/
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
{
$localeInfo = $this->localeProvider->getLocaleInfo();
$currentLocaleCode = $localeInfo->getLocaleCode($localeInfo->getCurrentLocale());
$i18nName = sprintf('%s.%s', $name, $currentLocaleCode);
$i18nRoute = $this->getRouteCollection()->get($i18nName);
if (!$i18nRoute) {
throw new RouteNotFoundException('Translated route not found');
}
$parameters = array_merge($parameters, ['_locale' => $currentLocaleCode]);
return $this->getGenerator()->generate($name, $parameters, $referenceType);
}
/**
* Tries to match a URL path with a set of routes.
*
* @param string $path
*
* @throws RouteNotFoundException
*
* @return array
*/
public function match(string $path): array
{
$result = null;
$localeInfo = $this->localeResolver->resolveFromPath($path);
$locale = $localeInfo->getCurrentLocale();
$trimPath = rtrim($path, '\//');
foreach ($this->getRouteCollection() as $route) {
if (rtrim($route->getPath(), '\//') === $trimPath) {
$result = array_merge(
$this->defaultRouter->match($route->getDefault('_org_path')),
['_locale' => $locale]
);
break;
}
}
// for dynamic routes try path without the locale prefix on the default router
if ($result === null && strpos($path, sprintf('/%s/', $localeInfo->getLocaleCode($locale))) === 0) {
$result = array_merge(
$this->defaultRouter->match(substr($path, 3)),
['_locale' => $locale]
);
}
if (!$result) {
throw new ResourceNotFoundException('No redirect required.');
}
return $result;
}
}