Drupal → Программная реализация ЧПУ
Задача — из адреса в формате /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 с временным решением.
Комментарии
Оставить комментарий