Задача — из адреса в формате /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 с временным решением.
- Вставка в CKEditor видео из ВКонтакте и Rutube (расширение модуля CKEditor 5 Media Embed)
- Как из PhpStorm выполнить тест(ы)
- Как работает опция "Aggregation type" в настройках полей Views при включённой агрегации
- Создание сравнительной таблицы с значениями из EAV Field
- Препроцессинг настроек форматтера перед сохранением
Комментарии
Добавить комментарий