Custom Entity Forms on the Example of User Editing and Registration
When using CMS Drupal, you can obtain many elements necessary for the project implementation without writing even a single line of code. However, when you want to display, e.g. a user editing form in a custom way, then such a component must be subject to some programming works. In this article, I will tell you how to register and display custom modes for displaying entity forms in Drupal, and how it can be useful for your project.
In version 8, Drupal has introduced one of the important functionalities – namely the entity form display modes. In principle, for every field-based entity, i.e. those that implement the interface Drupal\Core\Entity\FieldableEntityInterface, you can configure fields on a default form. You can also add a new form display mode and activate it on the selected entity directly in the administration panel.
In the presented example, I will be using the new experimental Claro administration skin.
Initial configuration in the user interface
Every entity (such as built-in entities – node, taxonomy, user) has a built-in form display management mechanism. In the case of user type entities, you can access such a form under the following address:
/admin/config/people/accounts/form-display
In addition to the default display mode, you can use custom display modes by activating selected modes in the lower section:
If your list is empty, it means that you have not registered any custom mode for your entity in the CMS yet. You can do this by accessing the form under:
/admin/structure/display-modes/form/add
Then you click the name of the chosen entity. It is going to be a user in our case:
In the following form, you only provide the label and the machine name.
Registering the form mode
By default, in Drupal you are not able to easily display an entity form after it has been created and unlocked in the user interface. We have to explicitly "tell" your entity what class will be responsible for handling the display of your form. There are two hooks to use: hook_entity_type_build and hook_entity_type_alter.
An example hook implementation for a user entity might, therefore, looks like this:
/**
* Implements hook_entity_type_build().
*/
function my_module_entity_type_build(array &$entity_types) {
$form_modes = ['custom_form_mode_1', 'custom_form_mode_2'];
foreach ($form_modes as $mode) {
$entity_types['user']->setFormClass($mode, 'Drupal\user\ProfileForm');
}
}
Most of the built-in entities already have classes for handling forms, and we can use them successfully. It is important for our class to expand the class:
namespace Drupal\Core\Entity\ContentEntityForm;
Own page with a form
In order to display a custom entity form as an independent page with a form, you can use the option of creating your own routing that will automatically handle its loading. It is of key importance to set the _entity_form value in its configuration, thanks to which the appropriate class responsible for generating the form structure will be found.
An example routing configuration for a user form page could look like this:
my_custom_module.user.edit_form:
path: '/user/{user}/custom-form-mode-1'
defaults:
_entity_form: user.custom_form_mode_1
_title: 'User entity custom form mode 1'
requirements:
_permission: 'required permission goes here'
There is no need to declare any additional controller or a separate form.
Loading the form independently
Displaying an entity form on a separate page often does not meet the design expectations. Therefore there is a need for mechanisms to load it in other cases, e.g. as a block content or as an element of some built-in or custom template. In the case of user entities, it can be some simple form for editing the avatar or profile data to be displayed, e.g. in a block.
In order to achieve the goal here, it is necessary to perform several steps, which will carry out operations that are similar to those being implicitly performed when displaying your own page on the basis of routing, but in a much more general sense.
For starters, you will need one of the most frequently used services for managing entity types:
$entityTypeManager = \Drupal::entityTypeManager();
Of course, I recommend injecting the service if the implementation is in the class body.
This manager is able to show you the appropriate class responsible for displaying the form implementing \Drupal\Core\Entity\EntityFormInterface by providing the id parameter of the entity type and the form mode.
$entity_type_id = 'user';
$form_mode = 'my_custom_form_mode_2';
$userForm = $entityTypeManager->getEntityForm($entity_type_id, $form_mode);
Then you need to inform your entity form on which specific instance of this type of entity you will be working on. We can both load an existing entity and create a new one:
$existingUser = $entityTypeManager->getStorage('user')->load($user_id);
// or
$newUser = $entityTypeManager->getStorage('user')->create();
and then set it as a form entity:
$userForm->setEntity($existingUser);
// or
$userForm->setEntity($newUser);
With an entity form object prepared in this way, you can finally build on its basis the structure of the final form using the \Drupal::formBuilder() service.
$form = \Drupal::formBuilder()->getForm($userForm);
Such a form is a table that can be directly rendered anywhere in your code, in accordance with the documentation of the getForm method from the interface Drupal\Core\Form\FormBuilderInterface.
Useful modules
Unfortunately, in the Drupal ecosystem, there are not many modules that facilitate solving the problem we are considering. One of these is the Entityform blockmodule, which allows for displaying the form of a selected entity in a block. Another module that only indirectly solves the problem is Inline Entity Form, which provides a widget that allows displaying a field with an entity reference as an entity form. It is also worth to mention the Paragraphs module, which also provides a widget to display the form for a field with a reference to the paragraph entity.
Summary
Custom entity forms are a very convenient functionality but still require some Drupal developer work. Fortunately, it turns out that many ready-made mechanisms allow us to achieve the desired effect with a quick configuration of the .yml file or a few lines of code. The implementation of such a form on a dedicated page or in the form of an own block should not, therefore, cause any major problems. So, good luck!