Как изменить маршрут, определенный другим модулем?

14

Другими словами, что такое Drupl 8, эквивалентный hook_menu_alter () ?

Drupal 8 по-прежнему использует hook_menu () , но, как я вижу, информация, возвращаемая ловушкой , отличается от того, что ловушка возвратила в Drupal 7. Например, определение, данное в user_menu () для пользователя, следующее.

  $items['user'] = array(
    'title' => 'User account',
    'title callback' => 'user_menu_title',
    'weight' => -10,
    'route_name' => 'user_page',
    'menu_name' => 'account',
  );

Свойство route_name ссылается на запись в файле user.routing.yml .

user_page:
  pattern: '/user'
  defaults:
    _content: '\Drupal\user\Controller\UserController::userPage'
  requirements:
    _access: 'TRUE'

Это отличается от того, что сделано с Symphony, и меня смущает то, как модуль может изменять маршрут, определенный от другого пользователя.

Единственная функция, которая все еще вызывается, hook_menu_alter()это menu_router_build () , но эта функция по-прежнему содержит код, который необходимо обновить, поскольку она все еще использует устаревший drupal_alter().

  // Alter the menu as defined in modules, keys are like user/%user.
  drupal_alter('menu', $callbacks);
  foreach ($callbacks as $path => $router_item) {
    // If the menu item is a default local task and incorrectly references a
    // route, remove it.
    // @todo This may be removed later depending on the outcome of
    // http://drupal.org/node/1889790
    if (isset($router_item['type']) && $router_item['type'] == MENU_DEFAULT_LOCAL_TASK) {
      unset($callbacks[$path]['route_name']);
    }
    // If the menu item references a route, normalize the route information
    // into the old structure. Note that routes are keyed by name, not path,
    // so the path of the route takes precedence.
    if (isset($router_item['route_name'])) {
      $router_item['page callback'] = 'USES_ROUTE';
      $router_item['access callback'] = TRUE;
      $new_path = _menu_router_translate_route($router_item['route_name']);

      unset($callbacks[$path]);
      $callbacks[$new_path] = $router_item;
    }
  }
киамлалуно
источник

Ответы:

6

См. Изменение существующих маршрутов и добавление новых маршрутов на основе динамических ; у меня получилось удалить / выполнить поиск, обработанный модулем поиска. В моем случае я использовал следующий код.

<?php
/**
 * @file
 * Contains \Drupal\ua_sc_module\Routing\SearchAlterRouteSubscriber.
 */

namespace Drupal\ua_sc_module\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class SearchAlterRouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  public function alterRoutes(RouteCollection $collection) {
    // Remove the /search route.
    $collection->remove('search.view');
  }

}
ТЗМ
источник
12

Разрешить представлениям переопределять существующие элементы маршрутизации, просто используя существующие функциональные возможности.

Если вы хотите изменить информацию, прикрепленную к маршруту, а не меню, например, фактически используемый контроллер или требования (разрешение / роль и т. Д.), Вы можете использовать событие, предоставляемое Drupal:

  <?php

  use Drupal\Core\Routing\RouteBuildEvent;
  use Drupal\Core\Routing\RoutingEvents;
  use Symfony\Component\EventDispatcher\EventSubscriberInterface;

  class RouteSubscriber implements EventSubscriberInterface {

   /**
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
      $events[RoutingEvents::ALTER] = 'alterRoutes';
     return $events;
   }

  /**
   * Alters existing routes.
   *
   * @param \Drupal\Core\Routing\RouteBuildEvent $event
   *   The route building event.
   */
  public function alterRoutes(RouteBuildEvent $event) {
    // Fetch the collection which can be altered.
    $collection = $event->getRouteCollection();
    // The event is fired multiple times so ensure that the user_page route
    // is available.
    if ($route = $collection->get('user_page')) {
      // As example add a new requirement.
      $route->setRequirement('_role', 'anonymous');
    }
  }

  }

Кроме того, вы должны зарегистрировать службу с тегом «event_subscriber» для этого класса.

Дэниел Венер
источник
Подробные сведения о настройке регистрации службы см. В разделе « Изменение существующих маршрутов и добавление новых маршрутов на основе динамических» .
колан
7

С тех пор, как я задал этот вопрос, ядро ​​Drupal 8 изменилось, и некоторые проблемы с маршрутами были исправлены.

hook_menu()больше не используется из Drupal 8; маршруты, используемые модулем, определены в файле .routing.yml (например, user.routing.yml ). Альтернативные хуки все еще используются, но так hook_menu()как они больше не используются из ядра Drupal, они также hook_menu_alter()не используются.

Шаги, необходимые для изменения маршрута, определенного из других модулей, следующие:

  • Определить услугу

    services:
      mymodule.route_subscriber:
        class: Drupal\mymodule\Routing\RouteSubscriber
        tags:
          - { name: event_subscriber }
  • Создайте класс, который расширяет RouteSubscriberBaseкласс.

    namespace Drupal\mymodule\Routing;
    
    use Drupal\Core\Routing\RouteSubscriberBase;
    use Symfony\Component\Routing\RouteCollection;
    
    /**
     * Listens to the dynamic route events.
     */
    class RouteSubscriber extends RouteSubscriberBase {
    
      /**
       * {@inheritdoc}
       */
      protected function alterRoutes(RouteCollection $collection) {
        // Change the route associated with the user profile page (/user, /user/{uid}).
        if ($route = $collection->get('user.page')) {
          $route->setDefault('_controller', '\Drupal\mymodule\Controller\UserController::userPage');
        }
      }
    
    }

Обратите внимание, что по сравнению с более ранними выпусками Drupal 8 некоторые детали изменены.

  • Название маршрута изменено с user_pageнаuser.page
  • Требования , предъявляемые к этому маршруту изменились от _access: 'TRUE'до_user_is_logged_in: 'TRUE'
  • Свойство для установки контроллера маршрута изменился по сравнению с _contentдо_controller
киамлалуно
источник
Если вы хотите сопоставить несколько маршрутов, есть ли более элегантный способ, чем установить для переменной $ matched значение false, а затем выполнить итерацию по всем по одному с $collection->get()? Я не вижу никаких очевидных методов.
Уильям Туррелл
@WilliamTurrell Вы можете получить ArrayIteratorобъект с помощью RouteCollection::getIterator(); это упростило бы перебор всех элементов в $collection. Вы также можете получить все маршруты $collectionс помощью RouteCollection::all(), который возвращает массив.
kiamlaluno