<?php
declare(strict_types=1);
namespace App\Routing;
use App\Locale\LocaleResolver;
use App\Provider\ProductProvider;
use Denios\Data\Product\Product;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Router for products
*
* @author Michel Chowanski <michel.chowanski@bestit-online.de>
* @package App\Routing
*/
class ProductRouter extends AbstractCommerceRouter
{
/**
* Field for variant sku
*
* @var string
*/
public const VARIANT_FIELD = 'variant';
private const CATEGORY_ONLY_SLUG = 'kat';
private ProductProvider $productProvider;
private LocaleResolver $localeResolver;
/**
* ProductRouter constructor.
*
* @param UrlGeneratorInterface $urlGenerator
* @param LocaleResolver $localeResolver
* @param ProductProvider $productProvider
*/
public function __construct(
UrlGeneratorInterface $urlGenerator,
LocaleResolver $localeResolver,
ProductProvider $productProvider
) {
parent::__construct($urlGenerator);
$this->productProvider = $productProvider;
$this->localeResolver = $localeResolver;
}
/**
* @param string $path
* @return array
*/
public function match(string $path): array
{
$localeInfo = $this->localeResolver->resolveFromPath($path);
$attributes = [];
$matches = [];
preg_match('#(?<slug>/?[^/]+)(/(?<sku>[\w|-]{6,}))?#i', $localeInfo->getPath(), $matches);
$slug = $matches['slug'] ?? $localeInfo->getPath();
$sku = $matches['sku'] ?? null;
$attributes['sku'] = $sku;
$product = $this->productProvider->getProductBySlug($slug);
if (!$product && $sku && $matches['slug'] !== self::CATEGORY_ONLY_SLUG) {
$product = $this->productProvider->getProductBySku($sku);
if ($product) {
$attributes['redirect'] = true;
}
}
if (!$product) {
throw new ResourceNotFoundException('Path could not be matched.');
}
if ($localeInfo->getCurrentLocale() !== $localeInfo->getDefaultLocale()) {
$attributes['prefix'] = $localeInfo->getPrefix();
}
return $this->route($localeInfo->getCurrentLocale(), $product, $attributes);
}
/**
* Create route params
*
* @param string $locale
* @param Product $product
* @param array $attributes
* @return array
*/
protected function route(string $locale, Product $product, array $attributes = []): array
{
$variant = $product->master;
if ($attributes['sku'] !== null) {
$matchedVariants = array_filter($product->variants, static fn($variant) => $variant->sku === $attributes['sku']);
if (count($matchedVariants) === 0) {
throw new ResourceNotFoundException(sprintf('Variant with sku `%s` not found.', $attributes['sku']));
}
$variant = array_shift($matchedVariants);
}
if (array_key_exists('redirect', $attributes) && $attributes['redirect'] === true) {
$controller = 'App\Action\RedirectAction::__invoke';
$prefix = '';
if (array_key_exists('prefix', $attributes)) {
$prefix = '/' . $attributes['prefix'];
}
return [
'_controller' => $controller,
'_locale' => $locale,
'product' => $product,
'variant' => $variant,
'path' => $prefix . '/' . $product->slug . '/' . $attributes['sku'],
'permanent' => true,
];
}
if ($product->businessUnit === 'ES') {
$controller = 'App\Action\Product\GetDetailESAction::__invoke';
} else {
$controller = 'App\Action\Product\GetDetailAction::__invoke';
}
return [
'_controller' => $controller,
'_locale' => $locale,
'product' => $product,
'variant' => $variant,
];
}
/**
* Get redis storage prefix
*
* @return string
*/
protected function getPrefix(): string
{
return 'product';
}
/**
* Append SKU to path if given
*
* @param array $parameters
* @return array
*/
protected function buildPath(array &$parameters): array
{
$values = parent::buildPath($parameters);
if (isset($parameters[self::VARIANT_FIELD])) {
$values[] = $parameters[self::VARIANT_FIELD];
unset($parameters[self::VARIANT_FIELD]);
}
return $values;
}
}