Задача — из адреса в формате /catalog/notebooks?price_from=xxx&price_to=yyy, в котором /catalog/notebooks это синоним термина, а ?price_from=xxx&price_to=yyy динамические параметры, сделать человекопонятный адрес в формате /catalog/notebooks/price-xxx-yyy.
Теория есть у niklan'a, поэтому сразу к коду.
Для решения надо создать сервис с двумя методами:
processOutbound() — изменяет исходящие адреса в новый формат
processInbound() — изменяет входящие адреса из нового формата в старый
src/ModulenamePathProcessor.php:
class ModulenamePathProcessor implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request) {
// Convert path from "/catalog/foo/bar/price-123-456" to "/catalog/foo/bar?price_from=123&price_to=456"
if (strpos($path, '/catalog/') === 0 && preg_match('/\/price-([^\/]+)$/', $path, $matches)) {
// Delete price from path
$path = str_replace($matches[0], '', $path);
// Move price to $_GET
$price = explode('-', $matches[1]);
$request->query->set('price_from', $price[0]);
$request->query->set('price_to', $price[1]);
}
return $path;
}
/**
* {@inheritdoc}
*/
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
// Convert path from "/catalog/foo/bar?price_from=123&price_to=456" to "/catalog/foo/bar/price-123-456"
if (strpos($path, '/catalog/') === 0 && isset($options['query']['price_from']) && isset($options['query']['price_to'])) {
// Move price query params to path
$path .= '/price-' . $options['query']['price_from'] . '-' . $options['query']['price_to'];
// Delete price query params
unset($options['query']['price_from']);
unset($options['query']['price_to']);
}
return $path;
}
}
modulename.services.yml:
services:
modulename.path_processor:
class: Drupal\modulename\ModulenamePathProcessor
tags:
- { name: path_processor_inbound, priority: 200 } # Run before \Drupal\Core\PathProcessor\PathProcessorAlias::processInbound()
- { name: path_processor_outbound, priority: 200 } # Run after \Drupal\Core\PathProcessor\PathProcessorAlias::processOutbound()По аналогии можно сделать ЧПУ любой сложности.
Замечания:
— Метод processOutbound() надо использовать с осторожностью, потому что он вызывается для всех url-адресов на странице, а это несколько десятков, а то и сотен раз на каждый запрос.
— Результат processInbound() кэшируется (см. RouteProvider::getRouteCollectionForRequest()), поэтому после изменения метода надо очищать кэш.
— В processInbound() можно менять только $path и $request->query, другие данные, например $request->attributes, менять бесполезно (опять же из-за кэша).
Важное замечание: этот способ пока не работает с модулем Redirect и включённой опцией "Enforce clean and canonical URLs". При открытии страницы /catalog/notebooks/price-xxx-yyy будет происходит редирект на /catalog/notebooks. Issue с временным решением.
Комментарии
Добавить комментарий