A deep dive into Drupal 8 routing

  • View
    78

  • Download
    3

Embed Size (px)

Transcript

  • A Deep Dive into Drupal 8 Routing

    DrupalCamp MumbaiApril 1, 2017Naveen Valecha

  • About me

    Drupal: naveenvalecha Git Administer on Drupal.org Site Maintainer of groups.drupal.org Twitter: @_naveenvalecha_ Web: https://www.valechatech.net Drupal 5,6,7,8

    https://www.valechatech.net

  • Agenda

    What are Routes. Why we need them? Routes and Controllers Access checking on routes Custom Access Checkers CSRF Prevention on routes Altering routes Dynamic Routes Parameter Upcasting

  • Routing System

    hook_menu to Symfony2 routing Replace paths with route names for rendering

    links Converting all page callbacks to controllers New breadcrumb system, new menu link system,

    conversion of local tasks and actions to plugins

  • Routing System - Cont.

    menu links, local tasks, local actions, contextual links

    Split all the pieces from hook_menu into YAML files finally

    module.routing.yml module.links.menu.yml module.links.task.yml module.links.action.yml module.contextual.yml

  • hook_menu to Symfony Routing

    PHP array to multiple yaml files Performance improvements Developer Experience(DX) Clean Code Procedural to Object Oriented(OO)

  • D7 hook_menu

    $items['admin/appearance/settings'] = array(

    'title' => 'Settings',

    'description' => 'Configure default and theme specific settings.',

    'page callback' => 'drupal_get_form',

    'page arguments' => array('system_theme_settings'),

    'access arguments' => array('administer themes'),

    'type' => MENU_LOCAL_TASK,

    'file' => 'system.admin.inc',

    'weight' => 20,

    );

  • D8 system.routing.yml

    system.theme_settings: path: '/admin/appearance/settings' defaults: _form: '\Drupal\system\Form\ThemeSettingsForm' _title: 'Appearance settings' requirements: _permission: 'administer themes'

  • D8 system.links.task.yml

    system.theme_settings: route_name: system.theme_settings title: 'Settings' base_route: system.themes_page weight: 100

  • Route

    A route is a path which is defined for Drupal to return some sort of content on. For example, the default front page, '/node' is a route.

    Use mm.routing.yml for defining routes

  • Routes and Controllers

    The routing system is responsible for matching paths to controllers, and you define those relations in routes. You can pass on additional information to your controllers in the route. Access checking is integrated as well.

  • Route - Slugentity.node.preview:

    path: '/node/preview/{node_preview}/{view_mode_id}'

    defaults:

    _controller: '\Drupal\node\Controller\NodePreviewController::view'

    _title_callback: '\Drupal\node\Controller\NodePreviewController::title'

    requirements:

    _node_preview_access: '{node_preview}'

    options:

    parameters:

    node_preview:

    type: 'node_preview'

  • /*** Defines a controller to render a single node in preview.*/class NodePreviewController extends EntityViewController {

    /** * {@inheritdoc} */ public function view(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL) { $node_preview->preview_view_mode = $view_mode_id; $build = parent::view($node_preview, $view_mode_id);

    return $build; }}

  • example.content: path: '/example' defaults: _controller: '\Drupal\example\Controller\ExampleController::content' custom_arg: 12 requirements: _permission: 'access content'

    // ...public function content(Request $request, $custom_arg) { // Now can use $custom_arg (which will get 12 here) and $request.}

  • Routes Structure

    Path(*): /node/preview/{node_preview}/{view_mode_id} defaults(*)

    _controller: \Drupal\node\Controller\NodePreviewController::view

    _form: \Drupal\Core\Form\FormInterface _entity_view, _entity_form: _title(optional), _title_context(optional),

    _title_callback(optional)

  • Routes Structure

    methods(optional) Requirements

    _permission, _role, _access, _entity_access, _custom_access, _format, _content_type_format

    _module_dependencies _csrf_token

  • Access checking

    Permissionrequirements:

    _permission: 'administer content types'

    Role

    requirements: _role: 'administrator'

  • Access checking - Custom

    class NodeRevisionAccessCheck implements AccessInterface {

    public function access(Route $route, AccountInterface $account, $node_revision = NULL, NodeInterface $node = NULL) {

    if ($node_revision) {

    $node = $this->nodeStorage->loadRevision($node_revision);

    }

    $operation = $route->getRequirement('_access_node_revision');

    return AccessResult::allowedIf($node && $this->checkAccess($node, $account, $operation))->cachePerPermissions()->addCacheableDependency($node);

    }

    }

  • Route - CSRF Protection

    aggregator.feed_refresh:

    path: '/admin/config/services/aggregator/update/{aggregator_feed}'

    defaults:

    _controller: '\Drupal\aggregator\Controller\AggregatorController::feedRefresh'

    _title: 'Update items'

    requirements:

    _permission: 'administer news feeds'

    _csrf_token: 'TRUE'

  • Routes - Altering

    class NodeAdminRouteSubscriber extends RouteSubscriberBase {

    protected function alterRoutes(RouteCollection $collection) {

    if ($this->configFactory->get('node.settings')->get('use_admin_theme')) {

    foreach ($collection->all() as $route) {

    if ($route->hasOption('_node_operation_route')) {

    $route->setOption('_admin_route', TRUE);

    }

    }

    }

    }

    }

  • Dynamic Routes

    Image.routing.yml

    route_callbacks: - '\Drupal\image\Routing\ImageStyleRoutes::routes'

  • Dynamic Routes - Cont.

    class ImageStyleRoutes implements ContainerInjectionInterface { public function routes() { $routes = []; $directory_path = $this->streamWrapperManager->getViaScheme('public')->getDirectoryPath(); $routes['image.style_public'] = new Route( '/' . $directory_path . '/styles/{image_style}/{scheme}', [ '_controller' => 'Drupal\image\Controller\ImageStyleDownloadController::deliver', ], [ '_access' => 'TRUE', ] ); return $routes; }}

  • Route-Parameter Upcasting

    entity.node.preview:

    path: '/node/preview/{node_preview}/{view_mode_id}'

    defaults:

    _controller: '\Drupal\node\Controller\NodePreviewController::view'

    _title_callback: '\Drupal\node\Controller\NodePreviewController::title'

    requirements:

    _node_preview_access: '{node_preview}'

    options:

    parameters:

    node_preview:

    type: 'node_preview'

  • Route-Parameter Upcasting Cont.

    node.services.ymlnode_preview:

    class: Drupal\node\ParamConverter\NodePreviewConverter

    arguments: ['@user.private_tempstore']

    tags:

    - { name: paramconverter }

    lazy: true

  • Route-Parameter Upcasting Cont.

    class NodePreviewConverter implements ParamConverterInterface {

    public function convert($value, $definition, $name, array $defaults) {

    $store = $this->tempStoreFactory->get('node_preview');

    if ($form_state = $store->get($value)) {

    return $form_state->getFormObject()->getEntity();

    }

    }

    public function applies($definition, $name, Route $route) {

    if (!empty($definition['type']) && $definition['type'] == 'node_preview') {

    return TRUE;

    }

    return FALSE;

    }

  • Whats for Drupal 9?

    Consider having a single class for Match Dumper, Route Provider and Route Builder

    Automatically unserialize request data and serialize outgoing data

  • References

    http://symfony.com/doc/current/components/

    routing.html https://www.drupal.org/docs/8/api/routing-syste

    m

    http://symfony.com/doc/current/components/routing.htmlhttp://symfony.com/doc/current/components/routing.htmlhttp://symfony.com/doc/current/components/routing.htmlhttps://www.drupal.org/docs/8/api/routing-systemhttps://www.drupal.org/docs/8/api/routing-systemhttps://www.drupal.org/docs/8/api/routing-system

  • Questions?

  • THANK YOU!