11

I need to add a custom tab in user profile page. I defined my route like below:

mymodule.routing.yml

mymodule.account:
path: '/user/{user}/custom'
defaults: 
  _form: '\Drupal\mymodule\Form\MyModuleUserSettingsForm'
  _title: 'Custom Settings'
  user: \d+
requirements:
  _permission: 'access content'

mymodule.links.task.yml

mymodule.account:
  title: Mymodule Settings
  route_name: mymodule.account
  base_route: entity.user.canonical

mymodule.links.menu.yml

mymodule.account:
  title: My module Settings
  parent: entity.user.canonical
  route_name: mymodule.account

Then after clear cache tab is appearing in the profile page. But when I open the url /user/1/custom I see the page not found message.

  • 1
    user: \d+ should go under requirements - make that change, clear all cache, see if you still get page not found. – Kevin Jul 29 '16 at 19:27
5
+25

Your problem is in the mymodule.routing.yml file, the big problem is the place of user: \d+, this line must be under the requirements: section, the other problem is the indentation. So the final code should be:

mymodule.account:
  path: '/user/{user}/custom'
  defaults: 
    _form: '\Drupal\mymodule\Form\MyModuleUserSettingsForm'
    _title: 'Custom Settings'
  requirements:
    _permission: 'access content'
    user: \d+

And of course you need the form class definition in src/Form/MyModuleUserSettingsForm.php

<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Class MyModuleUserSettingsForm.
 *
 * @package Drupal\mymodule\Form
 */
class MyModuleUserSettingsForm extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'simple_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['title'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Title'),
      '#maxlength' => 64,
      '#size' => 64,
      '#required' => TRUE,
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => t('Submit'),
    ];

    return $form;
  }

  public function validateForm(array &$form, FormStateInterface $form_state) {  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {  }

}

And a mymodule.info.yml file to complete your module (in this case named mymodule)

name: My Module
type: module
description: 'My module'
core: 8.x
package: Custom

Reference: Structure of routes

  • Interesting that the Structure of routes does not mention the key user: nor where it should go... Perhaps the docs need to be improved/edited? or did I miss something? – No Sssweat Feb 23 '17 at 6:16
  • nvm, I see, it's under _entity_access: and they used node as an example there, hence why my CTRL + F "user:" did not find anything. – No Sssweat Feb 23 '17 at 6:22
3

There are two ways to load a form using a route. You can either load up a callback that loads a form and returns it as part of the build array, or you can load up the form directly by setting the _form parameter under defaults.

You can search the codebase to find working examples, copy them into your mymodule.routing.yml, edit them to your needs and then rebuild the cache.

Loading form from callback:

There's a working example in the contact module:

/core/modules/contact/contact.routing.yml

entity.user.contact_form:
  path: '/user/{user}/contact'
  defaults:
    _title: 'Contact'
    _controller: '\Drupal\contact\Controller\ContactController::contactPersonalPage'
  requirements:
    _access_contact_personal_tab: 'TRUE'
    user: \d+

Then in /core/modules/contact/src/Controller/ContactController.php

you can see an example of how to load a form in the callback:

  public function contactPersonalPage(UserInterface $user) {
    // Do not continue if the user does not have an email address configured.
    if (!$user->getEmail()) {
      throw new NotFoundHttpException();
    }

    $message = $this->entityManager()->getStorage('contact_message')->create(array(
      'contact_form' => 'personal',
      'recipient' => $user->id(),
    ));

    $form = $this->entityFormBuilder()->getForm($message);
    $form['#title'] = $this->t('Contact @username', array('@username' => $user->getDisplayName()));
    $form['#cache']['contexts'][] = 'user.permissions';
    return $form;
  }

Loading form directly from the route:

If you would like to load the form directly using the _form default, there's an example in the shortcut module at /core/modules/shortcut/shortcut.routing.yml

shortcut.set_switch:
  path: '/user/{user}/shortcuts'
  defaults:
    _form: 'Drupal\shortcut\Form\SwitchShortcutSet'
    _title: 'Shortcuts'
  requirements:
    _custom_access: 'Drupal\shortcut\Form\SwitchShortcutSet::checkAccess'
  options:
    _admin_route: TRUE
    user: \d+

In this case, the user is passed in as a parameter to the form, see /core/modules/shortcut/src/Form/SwitchShortcutSet.php

  public function buildForm(array $form, FormStateInterface $form_state, UserInterface $user = NULL) {
1

A few things I can spot off hand...

You've got EVERYTHING named mymodule.account. I'd diversify that a bit. Consider going with this for the tasks page:

mymodule.account_tab:
  title: Mymodule Settings
  route_name: mymodule.account
  base_route: entity.user.canonical

I also do not believe you need anything in the menu either for this.

Between those two, you should be good to go! Feel free to reach out to me directly if you can't figure this out because I --JUST-- got this to work on my D8 port of Apply For Role!

1

In modulename.routing.yml you have to pass user argument like below

profile.user_information:
  path: '/user/{user}/profile'
  defaults:
    _form: '\Drupal\profile\Form\UserInformation'
    _title: 'UserInformation'
  requirements:
    _permission: 'access content'
  options:
    user: \d+

and in modulename.links.task.yml you have the code like below

profile.user_information:
  title: User profile
  route_name: profile.user_information
  base_route: entity.user.canonical

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.