patternManager = $patternManager; $this->tablePrefix = $tablePrefix; $this->moduleName = $moduleName; $this->idShop = $idShop ?: (int) \Context::getContext()->shop->id; $this->idLang = $idLang ?: (int) \Context::getContext()->language->id; } /** * Get the pattern manager instance * * @return UrlPatternManager */ public function getPatternManager() { return $this->patternManager; } /** * Resolve a URL to an entity * * @param string $url The request URI (without domain, with or without language prefix) * @return array|null [entity_type, id_entity, id_lang, id_product_attribute, controller] or redirect info */ public function resolve($url) { $url = $this->normalizeUrl($url); if (empty($url)) { return null; } // 1. Check URL cache for exact match (fastest) $cached = $this->lookupCachedUrl($url); if ($cached) { return $cached; } // 2. Check custom overrides $override = $this->lookupOverride($url); if ($override) { return $override; } // 3. Check URL history for redirects $redirect = $this->patternManager->findRedirect($url, $this->idLang); if ($redirect) { return [ 'redirect' => true, 'redirect_url' => $redirect['new_url'], 'redirect_type' => $redirect['redirect_type'], ]; } // 4. Try pattern matching (slowest, but handles dynamic URLs) $matched = $this->matchPattern($url); if ($matched) { return $matched; } return null; } /** * Normalize URL for matching * * @param string $url * @return string */ protected function normalizeUrl($url) { // Remove query string if (($pos = strpos($url, '?')) !== false) { $url = substr($url, 0, $pos); } $url = ltrim($url, '/'); // Remove language prefix if present $languages = \Language::getLanguages(true, $this->idShop); foreach ($languages as $lang) { $prefix = $lang['iso_code'] . '/'; if (strpos($url, $prefix) === 0) { $url = substr($url, strlen($prefix)); $this->idLang = (int) $lang['id_lang']; break; } } $url = rtrim($url, '/'); // Remove common suffixes for matching $suffixes = ['.html', '.htm', '.php']; foreach ($suffixes as $suffix) { if (substr($url, -strlen($suffix)) === $suffix) { $url = substr($url, 0, -strlen($suffix)); break; } } return strtolower($url); } /** * Lookup URL in cache table * * @param string $url * @return array|null */ protected function lookupCachedUrl($url) { $urlVariants = [$url, $url . '.html', $url . '/']; foreach ($urlVariants as $variant) { $hash = md5($variant); $result = \Db::getInstance()->getRow(' SELECT entity_type, id_entity, id_lang FROM `' . _DB_PREFIX_ . $this->tablePrefix . 'url_cache` WHERE url_hash = "' . pSQL($hash) . '" AND id_shop = ' . (int) $this->idShop . ' '); if ($result) { return [ 'entity_type' => $result['entity_type'], 'id_entity' => (int) $result['id_entity'], 'id_lang' => (int) $result['id_lang'], 'controller' => $this->getControllerForEntityType($result['entity_type']), 'id_product_attribute' => 0, ]; } } return null; } /** * Lookup URL in overrides table * * @param string $url * @return array|null */ protected function lookupOverride($url) { $urlVariants = [$url, $url . '.html', $url . '/']; foreach ($urlVariants as $variant) { $hash = md5($variant); $result = \Db::getInstance()->getRow(' SELECT entity_type, id_entity, id_lang, target_entity_type, target_id_entity, redirect_type, target_custom_url FROM `' . _DB_PREFIX_ . $this->tablePrefix . 'url_override` WHERE custom_url_hash = "' . pSQL($hash) . '" AND id_shop = ' . (int) $this->idShop . ' '); if ($result) { // Redirect to target entity if (!empty($result['target_entity_type']) && !empty($result['target_id_entity'])) { $targetUrl = $this->resolveEntityUrl( $result['target_entity_type'], (int) $result['target_id_entity'], (int) $result['id_lang'] ); if ($targetUrl) { return [ 'redirect' => true, 'redirect_url' => $targetUrl, 'redirect_type' => $result['redirect_type'] ?: '301', ]; } } // Redirect to custom URL if (!empty($result['target_custom_url'])) { return [ 'redirect' => true, 'redirect_url' => $result['target_custom_url'], 'redirect_type' => $result['redirect_type'] ?: '301', ]; } // Legacy override: serve entity at custom URL return [ 'entity_type' => $result['entity_type'], 'id_entity' => (int) $result['id_entity'], 'id_lang' => (int) $result['id_lang'], 'controller' => $this->getControllerForEntityType($result['entity_type']), 'id_product_attribute' => 0, ]; } } return null; } /** * Resolve a target entity to its front-office URL (relative path) * * @param string $entityType * @param int $idEntity * @param int $idLang * @return string|null */ protected function resolveEntityUrl($entityType, $idEntity, $idLang) { $context = \Context::getContext(); $link = $context->link; try { switch ($entityType) { case 'product': $url = $link->getProductLink($idEntity, null, null, null, $idLang); break; case 'category': $url = $link->getCategoryLink($idEntity, null, $idLang); break; case 'cms': $cmsObj = new \CMS($idEntity, $idLang); $url = $link->getCMSLink($cmsObj, null, null, $idLang); break; case 'manufacturer': $url = $link->getManufacturerLink($idEntity, null, $idLang); break; case 'supplier': $url = $link->getSupplierLink($idEntity, null, $idLang); break; default: return null; } $baseUrl = $context->shop->getBaseURL(true); if (strpos($url, $baseUrl) === 0) { return ltrim(substr($url, strlen($baseUrl)), '/'); } return ltrim(parse_url($url, PHP_URL_PATH), '/'); } catch (\Exception $e) { return null; } } /** * Try to match URL against patterns * * @param string $url * @return array|null */ protected function matchPattern($url) { $entityTypes = UrlPatternManager::getSupportedEntityTypes(); foreach ($entityTypes as $entityType) { if (!$this->patternManager->isEnabled($entityType)) { continue; } $pattern = $this->patternManager->getPattern($entityType); $regex = $this->patternToRegex($pattern['pattern'], $entityType); if (preg_match($regex, $url, $matches)) { $entity = $this->findEntityByPatternMatch($entityType, $matches); if ($entity) { return $entity; } } } return null; } /** * Convert pattern to regex for matching * * @param string $pattern * @param string $entityType * @return string */ protected function patternToRegex($pattern, $entityType) { $cacheKey = $this->tablePrefix . $entityType . '_' . md5($pattern); if (isset(self::$routePatterns[$cacheKey])) { return self::$routePatterns[$cacheKey]; } $regex = preg_quote($pattern, '#'); $placeholders = [ '\\{id\\}' => '(?P[0-9]+)', '\\{name\\}' => '(?P[a-z0-9\-]+)', '\\{rewrite\\}' => '(?P[a-z0-9\-]+)', '\\{reference\\}' => '(?P[a-z0-9\-]+)', '\\{ean13\\}' => '(?P[0-9]{13})?', '\\{upc\\}' => '(?P[0-9]{12})?', '\\{category\\}' => '(?P[a-z0-9\-]+)', '\\{categories\\}' => '(?P[a-z0-9\-\/]+)', '\\{brand\\}' => '(?P[a-z0-9\-]+)', '\\{supplier\\}' => '(?P[a-z0-9\-]+)', '\\{parent\\}' => '(?P[a-z0-9\-]+)', '\\{parents\\}' => '(?P[a-z0-9\-\/]+)', '\\{id_product_attribute\\}' => '(?P[0-9]+)?', '\\{attributes\\}' => '(?P[a-z0-9\-]+)?', '\\{tags\\}' => '(?P[a-z0-9\-]+)?', '\\{meta_keywords\\}' => '(?P[a-z0-9\-]+)?', '\\{meta_title\\}' => '(?P[a-z0-9\-]+)?', ]; $regex = preg_replace('#\\\\\\{attribute\\:[^}]+\\\\\\}#', '(?P[a-z0-9\-]+)?', $regex); foreach ($placeholders as $placeholder => $replacement) { $regex = preg_replace('#' . $placeholder . '#', $replacement, $regex); } $regex = preg_replace('#\\\\\\{[^}]+\\\\\\}#', '[a-z0-9\-]*', $regex); $regex = '#^' . $regex . '$#i'; self::$routePatterns[$cacheKey] = $regex; return $regex; } /** * Find entity by pattern match data * * @param string $entityType * @param array $matches * @return array|null */ protected function findEntityByPatternMatch($entityType, array $matches) { switch ($entityType) { case 'product': return $this->findProductByMatch($matches); case 'category': return $this->findCategoryByMatch($matches); case 'cms': return $this->findCmsByMatch($matches); case 'cms_category': return $this->findCmsCategoryByMatch($matches); case 'manufacturer': return $this->findManufacturerByMatch($matches); case 'supplier': return $this->findSupplierByMatch($matches); default: return null; } } /** * Find product by pattern match * * @param array $matches * @return array|null */ protected function findProductByMatch(array $matches) { if (!empty($matches['id'])) { $idProduct = (int) $matches['id']; if ($this->productExists($idProduct)) { return [ 'entity_type' => 'product', 'id_entity' => $idProduct, 'id_lang' => $this->idLang, 'controller' => 'product', 'id_product_attribute' => !empty($matches['id_product_attribute']) ? (int) $matches['id_product_attribute'] : 0, ]; } } if (!empty($matches['name'])) { $sql = ' SELECT p.id_product FROM `' . _DB_PREFIX_ . 'product` p INNER JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON ps.id_product = p.id_product AND ps.id_shop = ' . (int) $this->idShop . ' INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON pl.id_product = p.id_product AND pl.id_lang = ' . (int) $this->idLang . ' AND pl.id_shop = ' . (int) $this->idShop . ' WHERE pl.link_rewrite = "' . pSQL($matches['name']) . '" AND ps.active = 1 '; if (!empty($matches['category'])) { $sql .= ' AND EXISTS ( SELECT 1 FROM `' . _DB_PREFIX_ . 'category_lang` cl WHERE cl.id_category = p.id_category_default AND cl.id_lang = ' . (int) $this->idLang . ' AND cl.link_rewrite = "' . pSQL($matches['category']) . '" )'; } if (!empty($matches['brand'])) { $sql .= ' AND EXISTS ( SELECT 1 FROM `' . _DB_PREFIX_ . 'manufacturer` m WHERE m.id_manufacturer = p.id_manufacturer AND LOWER(REPLACE(m.name, " ", "-")) = "' . pSQL($matches['brand']) . '" )'; } $sql .= ' LIMIT 1'; $idProduct = (int) \Db::getInstance()->getValue($sql); if ($idProduct) { return [ 'entity_type' => 'product', 'id_entity' => $idProduct, 'id_lang' => $this->idLang, 'controller' => 'product', 'id_product_attribute' => !empty($matches['id_product_attribute']) ? (int) $matches['id_product_attribute'] : 0, ]; } } if (!empty($matches['ean13'])) { $idProduct = (int) \Db::getInstance()->getValue(' SELECT p.id_product FROM `' . _DB_PREFIX_ . 'product` p INNER JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON ps.id_product = p.id_product AND ps.id_shop = ' . (int) $this->idShop . ' WHERE p.ean13 = "' . pSQL($matches['ean13']) . '" AND ps.active = 1 LIMIT 1 '); if ($idProduct) { return [ 'entity_type' => 'product', 'id_entity' => $idProduct, 'id_lang' => $this->idLang, 'controller' => 'product', 'id_product_attribute' => 0, ]; } } if (!empty($matches['reference'])) { $idProduct = (int) \Db::getInstance()->getValue(' SELECT p.id_product FROM `' . _DB_PREFIX_ . 'product` p INNER JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON ps.id_product = p.id_product AND ps.id_shop = ' . (int) $this->idShop . ' WHERE LOWER(p.reference) = "' . pSQL(strtolower($matches['reference'])) . '" AND ps.active = 1 LIMIT 1 '); if ($idProduct) { return [ 'entity_type' => 'product', 'id_entity' => $idProduct, 'id_lang' => $this->idLang, 'controller' => 'product', 'id_product_attribute' => 0, ]; } } return null; } /** * Find category by pattern match * * @param array $matches * @return array|null */ protected function findCategoryByMatch(array $matches) { if (!empty($matches['id'])) { $idCategory = (int) $matches['id']; if ($this->categoryExists($idCategory)) { return [ 'entity_type' => 'category', 'id_entity' => $idCategory, 'id_lang' => $this->idLang, 'controller' => 'category', 'id_product_attribute' => 0, ]; } } if (!empty($matches['name'])) { $sql = ' SELECT c.id_category FROM `' . _DB_PREFIX_ . 'category` c INNER JOIN `' . _DB_PREFIX_ . 'category_shop` cs ON cs.id_category = c.id_category AND cs.id_shop = ' . (int) $this->idShop . ' INNER JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON cl.id_category = c.id_category AND cl.id_lang = ' . (int) $this->idLang . ' AND cl.id_shop = ' . (int) $this->idShop . ' WHERE cl.link_rewrite = "' . pSQL($matches['name']) . '" AND c.active = 1 '; if (!empty($matches['parent'])) { $sql .= ' AND EXISTS ( SELECT 1 FROM `' . _DB_PREFIX_ . 'category_lang` pcl WHERE pcl.id_category = c.id_parent AND pcl.id_lang = ' . (int) $this->idLang . ' AND pcl.link_rewrite = "' . pSQL($matches['parent']) . '" )'; } $sql .= ' LIMIT 1'; $idCategory = (int) \Db::getInstance()->getValue($sql); if ($idCategory) { return [ 'entity_type' => 'category', 'id_entity' => $idCategory, 'id_lang' => $this->idLang, 'controller' => 'category', 'id_product_attribute' => 0, ]; } } return null; } /** * Find CMS page by pattern match * * @param array $matches * @return array|null */ protected function findCmsByMatch(array $matches) { if (!empty($matches['id'])) { $idCms = (int) $matches['id']; if ($this->cmsExists($idCms)) { return [ 'entity_type' => 'cms', 'id_entity' => $idCms, 'id_lang' => $this->idLang, 'controller' => 'cms', 'id_product_attribute' => 0, ]; } } if (!empty($matches['name'])) { $idCms = (int) \Db::getInstance()->getValue(' SELECT c.id_cms FROM `' . _DB_PREFIX_ . 'cms` c INNER JOIN `' . _DB_PREFIX_ . 'cms_shop` cs ON cs.id_cms = c.id_cms AND cs.id_shop = ' . (int) $this->idShop . ' INNER JOIN `' . _DB_PREFIX_ . 'cms_lang` cl ON cl.id_cms = c.id_cms AND cl.id_lang = ' . (int) $this->idLang . ' AND cl.id_shop = ' . (int) $this->idShop . ' WHERE cl.link_rewrite = "' . pSQL($matches['name']) . '" AND c.active = 1 LIMIT 1 '); if ($idCms) { return [ 'entity_type' => 'cms', 'id_entity' => $idCms, 'id_lang' => $this->idLang, 'controller' => 'cms', 'id_product_attribute' => 0, ]; } } return null; } /** * Find CMS category by pattern match * * @param array $matches * @return array|null */ protected function findCmsCategoryByMatch(array $matches) { if (!empty($matches['id'])) { return [ 'entity_type' => 'cms_category', 'id_entity' => (int) $matches['id'], 'id_lang' => $this->idLang, 'controller' => 'cms', 'id_product_attribute' => 0, ]; } if (!empty($matches['name'])) { $idCmsCategory = (int) \Db::getInstance()->getValue(' SELECT cc.id_cms_category FROM `' . _DB_PREFIX_ . 'cms_category` cc INNER JOIN `' . _DB_PREFIX_ . 'cms_category_shop` ccs ON ccs.id_cms_category = cc.id_cms_category AND ccs.id_shop = ' . (int) $this->idShop . ' INNER JOIN `' . _DB_PREFIX_ . 'cms_category_lang` ccl ON ccl.id_cms_category = cc.id_cms_category AND ccl.id_lang = ' . (int) $this->idLang . ' AND ccl.id_shop = ' . (int) $this->idShop . ' WHERE ccl.link_rewrite = "' . pSQL($matches['name']) . '" AND cc.active = 1 LIMIT 1 '); if ($idCmsCategory) { return [ 'entity_type' => 'cms_category', 'id_entity' => $idCmsCategory, 'id_lang' => $this->idLang, 'controller' => 'cms', 'id_product_attribute' => 0, ]; } } return null; } /** * Find manufacturer by pattern match * * @param array $matches * @return array|null */ protected function findManufacturerByMatch(array $matches) { if (!empty($matches['id'])) { return [ 'entity_type' => 'manufacturer', 'id_entity' => (int) $matches['id'], 'id_lang' => $this->idLang, 'controller' => 'manufacturer', 'id_product_attribute' => 0, ]; } if (!empty($matches['name'])) { $idManufacturer = (int) \Db::getInstance()->getValue(' SELECT m.id_manufacturer FROM `' . _DB_PREFIX_ . 'manufacturer` m INNER JOIN `' . _DB_PREFIX_ . 'manufacturer_shop` ms ON ms.id_manufacturer = m.id_manufacturer AND ms.id_shop = ' . (int) $this->idShop . ' WHERE LOWER(REPLACE(REPLACE(m.name, " ", "-"), "\'", "")) = "' . pSQL($matches['name']) . '" AND m.active = 1 LIMIT 1 '); if ($idManufacturer) { return [ 'entity_type' => 'manufacturer', 'id_entity' => $idManufacturer, 'id_lang' => $this->idLang, 'controller' => 'manufacturer', 'id_product_attribute' => 0, ]; } } return null; } /** * Find supplier by pattern match * * @param array $matches * @return array|null */ protected function findSupplierByMatch(array $matches) { if (!empty($matches['id'])) { return [ 'entity_type' => 'supplier', 'id_entity' => (int) $matches['id'], 'id_lang' => $this->idLang, 'controller' => 'supplier', 'id_product_attribute' => 0, ]; } if (!empty($matches['name'])) { $idSupplier = (int) \Db::getInstance()->getValue(' SELECT s.id_supplier FROM `' . _DB_PREFIX_ . 'supplier` s INNER JOIN `' . _DB_PREFIX_ . 'supplier_shop` ss ON ss.id_supplier = s.id_supplier AND ss.id_shop = ' . (int) $this->idShop . ' WHERE LOWER(REPLACE(REPLACE(s.name, " ", "-"), "\'", "")) = "' . pSQL($matches['name']) . '" AND s.active = 1 LIMIT 1 '); if ($idSupplier) { return [ 'entity_type' => 'supplier', 'id_entity' => $idSupplier, 'id_lang' => $this->idLang, 'controller' => 'supplier', 'id_product_attribute' => 0, ]; } } return null; } /** * Check if product exists * * @param int $idProduct * @return bool */ protected function productExists($idProduct) { return (bool) \Db::getInstance()->getValue( 'SELECT 1 FROM `' . _DB_PREFIX_ . 'product_shop` WHERE id_product = ' . (int) $idProduct . ' AND id_shop = ' . (int) $this->idShop . ' AND active = 1' ); } /** * Check if category exists * * @param int $idCategory * @return bool */ protected function categoryExists($idCategory) { return (bool) \Db::getInstance()->getValue( 'SELECT 1 FROM `' . _DB_PREFIX_ . 'category_shop` WHERE id_category = ' . (int) $idCategory . ' AND id_shop = ' . (int) $this->idShop ); } /** * Check if CMS page exists * * @param int $idCms * @return bool */ protected function cmsExists($idCms) { return (bool) \Db::getInstance()->getValue( 'SELECT 1 FROM `' . _DB_PREFIX_ . 'cms_shop` WHERE id_cms = ' . (int) $idCms . ' AND id_shop = ' . (int) $this->idShop ); } /** * Get controller name for entity type * * @param string $entityType * @return string */ public function getControllerForEntityType($entityType) { $controllers = [ 'product' => 'product', 'category' => 'category', 'cms' => 'cms', 'cms_category' => 'cms', 'manufacturer' => 'manufacturer', 'supplier' => 'supplier', ]; return $controllers[$entityType] ?? 'index'; } /** * Generate PrestaShop-compatible route definitions for moduleRoutes hook * * @return array */ public function generateModuleRoutes() { $routes = []; $entityTypes = UrlPatternManager::getSupportedEntityTypes(); $routePrefix = str_replace('_', '', $this->tablePrefix); foreach ($entityTypes as $entityType) { if (!$this->patternManager->isEnabled($entityType)) { continue; } $pattern = $this->patternManager->getPattern($entityType); $routeRule = $this->patternToRouteRule($pattern['pattern'], $pattern); $controller = $this->getControllerForEntityType($entityType); $languages = \Language::getLanguages(true, $this->idShop); foreach ($languages as $lang) { $routeKey = $routePrefix . '_' . $entityType . '_' . $lang['iso_code']; $routes[$routeKey] = [ 'controller' => $controller, 'rule' => $lang['iso_code'] . '/' . $routeRule, 'keywords' => $this->getRouteKeywords($entityType), 'params' => [ 'fc' => 'module', 'module' => $this->moduleName, ], ]; } } return $routes; } /** * Convert pattern to PrestaShop route rule * * @param string $pattern * @param array $config * @return string */ protected function patternToRouteRule($pattern, array $config) { $conversions = [ '{id}' => '{id}', '{name}' => '{rewrite}', '{rewrite}' => '{rewrite}', '{category}' => '{category}', '{categories}' => '{categories}', '{brand}' => '{manufacturer}', '{supplier}' => '{supplier}', '{ean13}' => '{ean13}', '{reference}' => '{reference}', '{parent}' => '{parent}', '{parents}' => '{parents}', '{id_product_attribute}' => '{id_product_attribute}', '{attributes}' => '{attributes}', ]; $rule = $pattern; foreach ($conversions as $from => $to) { $rule = str_replace($from, $to, $rule); } $rule = preg_replace('/\{attribute:[^}]+\}/', '', $rule); $rule = preg_replace('/\{[^}]+\}/', '', $rule); if (!empty($config['suffix'])) { $rule .= $config['suffix']; } $rule = preg_replace('#/+#', '/', $rule); $rule = trim($rule, '/'); return $rule; } /** * Get route keywords for entity type * * @param string $entityType * @return array */ public function getRouteKeywords($entityType) { $baseKeywords = [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_' . $entityType], 'rewrite' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*', 'param' => 'rewrite'], ]; switch ($entityType) { case 'product': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_product'], 'category' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'categories' => ['regexp' => '[_a-zA-Z0-9\pL\pS\/-]*'], 'ean13' => ['regexp' => '[0-9]*'], 'reference' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'manufacturer' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'supplier' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'id_product_attribute' => ['regexp' => '[0-9]*', 'param' => 'id_product_attribute'], 'attributes' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'price' => ['regexp' => '[0-9\-\.]*'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'tags' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); case 'category': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_category'], 'parent' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'parents' => ['regexp' => '[_a-zA-Z0-9\pL\pS\/-]*'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); case 'cms': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_cms'], 'category' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'categories' => ['regexp' => '[_a-zA-Z0-9\pL\pS\/-]*'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); case 'cms_category': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_cms_category'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); case 'manufacturer': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_manufacturer'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); case 'supplier': return array_merge($baseKeywords, [ 'id' => ['regexp' => '[0-9]+', 'param' => 'id_supplier'], 'meta_title' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], 'meta_keywords' => ['regexp' => '[_a-zA-Z0-9\pL\pS-]*'], ]); default: return $baseKeywords; } } }