Provide a custom mode form to entities with Drupal 8

a colored mask

Like display modes that allow you to display an entity in multiple ways, Drupal 8 allows you to create multiple form modes that can be used on entities, whether they are users, taxonomy terms, contents or any custom entity. Let's discover here how to use these form modes, from their creation to their use to customize the input of a user's information, for example.

The creation of form modes is quite simple and can be done in a few clicks, from the administration interface (from the path /admin/structure/display-modes/form).

Drupal 8 form modes

Let's add a new form mode that we will call for example Profil.

Form mode Profil


And the User entity now has a new Profile form mode, in addition to the existing Register form mode (used for the registration form on a Drupal 8 site).

And we find our new form mode on the configuration page of the forms display (path /admin/config/people/accounts/form-display) of Drupal users.

Profil form

That we can activate so that we can then configure which fields will be rendered in this form mode. For example, we can configure this form mode to fill only the First name, Last name, Organization and Picture fields that have been created for the User entity.

Form profil configuration


So far so good. But how do we use our new form mode? From which path?

To finalize this we will use a small module that we will call my_module.

This module will allow us to declare our new form mode for the User entity, and to create a route, as well as a menu, which will allow us to access and complete our form.

First, let's declare this form mode and associate a Class with it, from the file my_module.module.

 * Implements hook_entity_type_build().
function my_module_entity_type_build(array &$entity_types) {
  $entity_types['user']->setFormClass('profil', 'Drupal\user\ProfileForm');

Here we associate the default form class User ProfileForm with our profil form mode. We could just as easily have used a Class MyProfilCustomForm by extending the Class AccountForm.

All we have to do now is create a route, from the file my_module.routing.yml, and we can then access our form.

  path: '/user/{user}/profil'
    _entity_form: 'user.profil'
    _title: 'Profil'
    _entity_access: 'user.update'
    _custom_access: '\Drupal\my_module\Access\MyModuleUserAccess::editProfil'
    user: \d+
    _admin_route: FALSE

From the route declaration, we specify the path (/user/{user}/profile), the form mode to be used for the User entity, specify route access rights (the right to modify a user, as well as customized permissions if necessary), and can also specify whether the route corresponds to an administration route, or not, to define the theme under which the form will be rendered (backoffice or frontoffice).

To finalize our new form mode, we will create a dynamic menu entry in the user account menu, in order to give an access link to users or administrators. In the file, let's add an entry to create the corresponding menu link.

  title: 'Profil'
  weight: 10
  route_name: my_module.user.profil
  base_route: entity.user.canonical
  menu_name: user-account
  class: Drupal\my_module\Plugin\Menu\ProfilUserBase

What is notable here, in this menu entry, is the class property that will allow us to define the dynamic {user} parameter of the route corresponding to this menu entry. 

This ProfileUserBase Class will only return the ID of the accessed user, if the menu link is displayed on the user's page, or return the ID of the connected user if it is not, otherwise.


namespace Drupal\my_module\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;
use Drupal\Core\Url;
use Drupal\user\UserInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Menu\StaticMenuLinkOverridesInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

 * Profile Menu Link
class ProfilUserBase extends MenuLinkDefault implements ContainerFactoryPluginInterface {

   * The entity type manager.
   * @var \Drupal\Core\Entity\EntityTypeManager
  protected $entityTypeManager;

   * The current route match service.
   * @var \Drupal\Core\Routing\CurrentRouteMatch
  protected $currentRouteMatch;

   * The current user.
   * @var \Drupal\Core\Session\AccountInterface
  protected $currentUser;

   * Constructs a new MenuLinkDefault.
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Menu\StaticMenuLinkOverridesInterface $static_override
   *   The static override storage.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param \Drupal\Core\Routing\RouteMatchInterface $current_route_match
   *   The current route match service.
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   The current user.
  public function __construct(array $configuration, $plugin_id, $plugin_definition, StaticMenuLinkOverridesInterface $static_override, EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $current_route_match, AccountInterface $current_user) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $static_override);
    $this->entityTypeManager = $entity_type_manager;
    $this->currentRouteMatch = $current_route_match;
    $this->currentUser = $current_user;

   * {@inheritdoc}
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(

  public function getRouteParameters() {
    return ['user' => $this->getUserIdFromRoute()];

   * {@inheritdoc}
  public function getCacheContexts() {
    return ['user', 'url'];

   * Get the Account user id from the request or fallback to current user.
   * @return int
  public function getUserIdFromRoute() {
    $user = $this->currentRouteMatch->getParameter('user');
    if ($user instanceof AccountInterface) {
     return $user->id();
    elseif (!empty($user)) {
      $user = $this->entityTypeManager->getStorage('user')->load($user);
      if($user instanceof AccountInterface) {
        return $user->id();

    return $this->currentUser->id();


And now you can use your new form mode that you can customize at will, either from the graphical interface or from a customized Form Class allowing you to introduce any business and/or complex logic easily.

Finally, I can't conclude this post on the Drupal 8 form mode without mentioning the Form Mode Manager module which can allow you to do all this without the need for a Drupal developer. Depending on your needs and the level of expertise you want, you can choose one or the other of these solutions. But this may be the subject of another blog post.


Ajouter un commentaire