Routing in Drupal 8
-
Upload
kgoel1 -
Category
Technology
-
view
273 -
download
8
Transcript of Routing in Drupal 8
Nice to Meet You!
Kalpana GoelDeveloper
Drupal.org - kgoel
Twitter - @kalpanagoel
William HurleyManager, Technical
Development
Routes are the mappings between URL paths and
their corresponding page and access callbacks.
What is a route?
hook_menu is dead in 8.0.x
There is no hook_menu in Drupal 8!
MODULENAME.routing.yml
One path may map to multiple routes
D7 hook_menu● Routing (page and access callbacks)
● Menu links
● Local actions
● Local tasks
● Breadcrumbs
● Contextual links
● Title
● Weight
D7: hook_menu()
function user_menu() {
$items['user/logout'] = array(
'title' => 'Log out',
'access callback' => 'user_is_logged_in',
'page callback' => 'user_logout',
'weight' => 10,
'menu_name' => 'user-menu',
'file' => 'user.pages.inc',
);
return $items;
}
D7: page callback
/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;
watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
module_invoke_all('user_logout', $user);
// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
drupal_goto();
}
D8: Routing
user.routing.yml
user.logout:
path: '/user/logout'
defaults:
_controller: '\Drupal\user\Controller\UserController::logout'
requirements:
_user_is_logged_in: 'TRUE'
D8: Controller
namespace Drupal\user\Controller;
class UserController extends ControllerBase {
public function logout() {
user_logout();
return $this->redirect('<front>');
}
D8: Path (required)
For dynamic properties, you can include them in curly braces.
For example -
‘/admin/structure/views/{js}/display/{view}/{display_id}/{type}'
The {display_id} element in the URL is called a placeholder and
is available as $display_id in the controller method.
D8: dynamic path example
views_ui.form_display:
path: '/admin/structure/views/{js}/display/{view}/{display_id}/{type}'
defaults:
_content: '\Drupal\views_ui\Form\Ajax\Display::getForm'
class Display extends ViewsFormBase {
public function getForm(ViewStorageInterface $view, $display_id, $js, $type =
NULL) {
$this->setType($type);
return parent::getForm($view, $display_id, $js);
}
D8: Optional Attributes
user.cancel_confirm:
path: '/user/{user}/cancel/confirm/{timestamp}/{hashed_pass}'
defaults:
_title: 'Confirm account cancellation'
_content: '\Drupal\user\Controller\UserController::confirmCancel'
timestamp: 0
hashed_pass: ''
D8: Page Title
user.view:
path: '/user/{user}'
defaults:
_entity_view: 'user.full'
_title_callback: 'Drupal\user\Controller\UserController::userTitle'
requirements:
_entity_access: 'user.view'
D8: Page Types
_content : - display content on a page
_form : - display form on a page.
_controller : - use to generate raw data like json output
_entity_view : - for example - node.teaser
_entity_form : - display a form for a entity
_entity_list : - display list of entity like node
D8: Access checking
user.admin_account:
path: '/admin/people'
defaults:
_entity_list: 'user'
_title: 'People'
requirements:
_permission: 'administer users'
D8: Access checkers
Based upon roles, permissions:
_permission - A permission string (e.g. - _permission: ‘access
content’)
_role : A specific user role (e.g.- administrator)
D8: Access checkers
Based upon access to Entities (view, update, delete)
_entity_access: In case where an entity is part of route, can check
a certain access level before granting access (e.g. node.view)
Example:
_entity_access: node.view
D8: Access checkCustom Access
_custom_access: You can also do custom access checking on
route.
Same as title callback (define as method on class)Read more -
https://www.drupal.org/node/2122195
_custom_access: Drupal\shortcut\Form\SwitchShortcutSet::checkAccess
public function checkAccess(UserInterface $user = NULL) {
return shortcut_set_switch_access($user);
}
D8: Access check
Multiple access check -
node.add_page:
path: '/node/add'
defaults:
_title: 'Add content'
_content:
'\Drupal\node\Controller\NodeController::addPage'
options:
_access_mode: 'ANY'
_node_operation_route: TRUE
requirements:
_permission: 'administer content types'
_node_add_access: 'node'
D7: Form Router
$items['user/password'] = array(
'title' => 'Request new password',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_pass'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'file' => 'user.pages.inc',
);
D7: User Password Form
function user_pass() {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Username or e-mail address'),
'#size' => 60,
'#maxlength' => max(USERNAME_MAX_LENGTH,
EMAIL_MAX_LENGTH),
'#required' => TRUE,
'#default_value' => isset($_GET['name']) ? $_GET['name'] : '',
);
[...]
}
function user_pass_validate($form, &$form_state)
function user_pass_submit($form, &$form_state)
D8: Form Router
Forms are classes
There is no method in forms as forms are presented as one class
Use _form instead of _content or _controller
user.pass:
path: '/user/password'
defaults:
_form: '\Drupal\user\Form\UserPasswordForm'
_title: 'Request new password'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
D8: Form Interface
namespace Drupal\Core\Form;
interface FormInterface {
public function getFormId() {
return 'user_pass';
}
D8: Form Interfacepublic function buildForm(array $form, FormStateInterface $form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Username or email address'),
'#size' => 60,
'#maxlength' => max(USERNAME_MAX_LENGTH,
Email::EMAIL_MAX_LENGTH),
'#required' => TRUE,
'#attributes' => array(
'autocorrect' => 'off',
'autocapitalize' => 'off',
'spellcheck' => 'false',
'autofocus' => 'autofocus',
),
);
D8: Form Interfacepublic function validateForm(array &$form, FormStateInterface $form_state) {
$name = trim($form_state->getValue('name'));
$users = $this->userStorage->loadByProperties(array('mail' => $name, 'status' =>
'1'));
if (empty($users)) {
$users = $this->userStorage->loadByProperties(array('name' => $name, 'status'
=> '1'));
}
if ($account && $account->id()) {
$form_state->setValueForElement(array('#parents' => array('account')),
$account);
}
else {
$form_state->setErrorByName('name', $this->t('Sorry, %name is not recognized
as a username or an email address.', array('%name' => $name)));
$account = reset($users);
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$langcode = $this->languageManager->getCurrentLanguage()->getId();
$account = $form_state->getValue('account');
// Mail one time login URL and instructions using current language.
$mail = _user_mail_notify('password_reset', $account, $langcode);
if (!empty($mail)) {
$this->logger('user')->notice('Password reset instructions mailed to %name
at %email.', array('%name' => $account->getUsername(), '%email' =>
$account->getEmail()));
drupal_set_message($this->t('Further instructions have been sent to your
email address.'));
}
$form_state->setRedirect('user.page');
}
D8: Form Base class
** generic base class - this includes string translation, link generator
Drupal\Core\Form\FormBase
for example -
class UserLoginForm extends FormBase
* * Base class for implementing system configuration forms.
Drupal\core\form\ConfigFormBase
for example -
class MenuSettingsForm extends ConfigFormBase
** base class for a confirmation form.
Drupal\Core\Form\ConfirmFormBase
for example -
class UserMultipleCancelConfirm extends ConfirmFormBase
D7: menu local tasks
$items['user/password'] = array(
'title' => 'Request new password',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_pass'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'file' => 'user.pages.inc',
);
D8: menu local tasks
user.links.task.yml
user.page:
route_name: user.page
base_route: user.page
title: 'Log in'
weight: -10
user.pass:
route_name: user.pass
base_route: user.page
title: 'Request new password'
D7: Local action
$items['admin/structure/types/add'] = array(
'title' => 'Add content type',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_type_form'),
'access arguments' => array('administer content types'),
'type' => MENU_LOCAL_ACTION,
'file' => 'content_types.inc',
);
D8: Local action
node.links.action.yml
node.add_page:
route_name: node.add_page
title: 'Add content'
appears_on:
- system.admin_content
D8: Local action on multiple pagesblock_content.links.action.yml
block_content_add_action:
route_name: block_content.add_page
title: 'Add custom block'
class:
\Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction
appears_on:
- block.admin_display
- block.admin_display_theme
- block_content.list
D7: Contextual
links$items['admin/structure/block/manage/%/%/configure'] = array(
'title' => 'Configure block',
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
);
D8: Contextual
links
block.links.contextual.yml
block_configure:
title: 'Configure block'
route_name: 'entity.block.edit_form'
group: 'block'
D8: Path based breadcrumb
breadcrumb is path based in Drupal 8
/node/add/content
/node/add
/node
/
https://www.drupal.org/node/2098323
Options
Useful Tips
_admin_route -- whether to use the admin theme for this route
_maintenance_access -- whether route is publicly available when the site
is in maintenance mode
_access_mode -- whether requirements are ANY or ALL
Useful links
https://www.drupal.org/node/1800686 - change record
https://www.drupal.org/node/2118147 - D7 to D8 upgrade tutorial
https://www.drupal.org/developing/api/8/routing - Routing system in D8