a deep dive into drupal 8 routing
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
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;
}
What’s 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
Questions?
THANK YOU!