Have you ever created a configuration form? It is one of the first things that we learn as Drupal developers. We use configuration forms to store values in configuration and then build functionalities based on those stored values. A good example is the ‘Basic site settings’ where we add the site name, site email, front page etc.
What is configuration validation?
Let's go back to the ‘Basic site settings’. Go to the ‘Error pages’ section, add ‘/i-love-drupal’ as the ‘Default 404 (not found) page’ and try to save the configuration.
![configuration validation](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa93d43ec5dac0dd61_AD_4nXe38NgcBWHZ90MbJ1El_XUnRABwG3ZhC7r9mLR36yzsTEJgZNmg_kT6DJuatABtDjucq5OiOnhZxVmm-qZxQ4gUYt-Ey1M7pc29CIXuRnEmCvItXRBbbSBUCR02OuxVIe5TvvneS1jNTmNQTe0K24NnzzPP.png)
You will get an error message saying that the path is not valid. Now create a page in your site with the alias ‘/i-love-drupal’ and try to save this configuration again.
![Path is not valid error message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa7d5f36314ccfdd3d_AD_4nXdovz1GFYh_xtUI_xhOr2IwB4EIXJThZElhHkPvMgJ2RSRdHVDdptqddJPKwnsUNQxtvaZkuWMgHXUGUU8NAKoMzhDCx3R4dtXsAUH37GQj_tQu2L72rOIoFfq9eqwyQTgVcF5KC1CxQ6339bwnYcmsIqQ.png)
You will be able to save the configuration now. What happened here? Drupal expects a valid URL as the ‘default 404 page URL’. So the user input is checked before the values are saved to the configuration to ensure that the value given is a valid URL. If it’s not, the configuration is not updated and an error message is displayed to the user.
Usually, such validations are added from the form class using the validateForm() method.
But now, we can also add such validations from the schema.yml file by using constraints, without using validateForm() method. A new property ‘#config_tagret’ was introduced in Drupal 10.2 to support such config validations.
How does it work?
- Create a simple configuration form with one text field. Keep it as a non-mandatory field.
- Values submitted by the user would be stored in my_custom_module.settings.yml.
- Now assume that when a user adds a value to the ‘name’ field, it should be stored under the ‘username’ key in my_custom_module.settings.yml. Traditionally, we use $config->set(‘username’, $form_state->getValue(‘name’)) In submitForm() method to do this. Now we can do this directly from the buildForm() method itself, by using the new ‘#config_target’ option.
- Create a schema file for this configuration.
- Add ‘#config_target’ property to the ‘name’ form element.
- '#config_target' => 'my_custom_module.settings:username' tells the form to store the value from the field under the ‘username’ key in my_custom_module.settings configuration.
- Now, open the form. Add a name and submit it.
![Saved status message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa3f33c87598606ed9_AD_4nXeZzLWqZ_L2mbh1Q34JhXABLcuZl13JlOqgtKNCSn3V6_bI-xDVXEoX_t56V5uwLS4AXcXYazK73VeKNgrSBP1KZaUhczq9nDU0-4zEu2-8tLKSLifg_lAPjlPHDOl5SZByigsHtHlhulgWnUiN4IEQnmnA.png)
- You will see that the value has been submitted.
- Export the configuration and check my_custom_module.settings.yml. The submitted value will be stored under the ‘username’ key.
![Value submission for configuration](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa555be51f7b0b186d_AD_4nXchrlH5RrPm5PM-WjlBXu-ExFwCBHHvs74vbBAFu6BXzT9nXJgkpQMUpyZBxxcVAmD-Jx0mo4aFSdr0vwpaEot-2ixy2su4RVpPYF1lUtW8-CvC7NlW8y7ka5-75dzbc7nNECxwoMNsLIKe3PIsxitlF2Y9.png)
- The field even renders the submitted value as the default value, even though we have not explicitly provided the ‘#default_value’ property. We haven’t even added the validateForm() and submitForm() methods. Isn’t that cool?
This looks great. But how to validate the user input?
- Validations are added using the constraints key in the schema.yml file.
- We can use the NotBlank constraint to make the name field a mandatory field.
- Now try to save the form without adding any value in the name field. The form will throw an error
![Error status message for blank field](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fb1aab7f101f417816_AD_4nXfu0JskGHaNjfDL64EM5I2hLIYkcdYF6d5zIn8nK_Zi0wfZuX78LoiYRFVGIu8ixb8UzHA0sComp2eQOdGVPUeFBN1wwcJR7f01qM1lijxsN2lw3Dsedn5bo-nV4u9zPddlLpM5MBxxLfkNvGNvSsRSAIIa.png)
- We could even customize the error message.
![Field inevitable error message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960f958c0bd3fbe3afdeb_AD_4nXfSySqIYmbM9R7HieScDDjUm1oZErIA6qLPswvDvEMEknoDYbz_dbjiJmtPqmTivWHnievq7bJwwYwBc53ysHymoSlbwEsg1HVHtkb1jUhSk9CBI3VkOIWlctRC8vAQVc5I4frDuKaIpyZRyDCjp1W-G_cA.png)
- Let's add one more text field ‘purpose’ to the form. The value from this field should be stored under the ‘purpose’ key in the ‘my_custom_module.settings’ config. So the config target will be ‘my_custom_module.settings:purpose’.
- Update the schema file and include data corresponding to this field. This time, let's add another validation constraint Choice to restrict the values to a specific set.
- Now, if you try to save the ‘purpose’ field with an invalid value, you will get an error.
![Not valid choice error message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fabaa95b72e5078260_AD_4nXeEqPiGHa7cLA2ZsXebcy99TdBL300gGo7t1zUVlHQNJ7PVjAGchj5Vl90bseM3ILFCT7L2Q03RVZfQ5v-VAI6q8Fgk6Lx0gSTxkuCJCB51Ra7vN4a-a5hh6HqgINbSlfxnX5pBb-gDL3a7HjB9Xz4fjho.png)
- There are many more types of constraints available such as Regex, NotNull, email, Length, etc. Multiple constraints can be added to validate a single field as well.
Eg:
- Commonly used constraints could be found in web/core/config/schema/core.data_types.schema.yml. We could also create our own constraints which we will explore towards the end of this blog.
Transforming config values using ‘#config_target’
- As we saw earlier, when '#config_target' => 'my_custom_module.settings:username' is added to a form field, it takes the user input and saves it under the ‘username’ key in ‘my_custom_module.settings’.
- But what if we want to transform the value before saving it in the config and vice versa? Let's check with an example.
- Add a checkbox field ‘future_plan’ and map it to the ‘future_plan’ key in the configuration.
- By default, when the user submits the form, either 1 or 0 would get saved in the configuration. We could also change it to ‘TRUE’ or ‘FALSE’ by changing the type to ‘boolean’ in the schema file.
- But what if we want to store something else in the config? For example, what if we need to store ‘YES! I WILL CONTINUE TO EXPLORE THE UNIVERSE’ when the user checks the checkbox and ‘NO! I WILL DESTROY THE STONES, RETIRE, AND BECOME A FARMER’ if the user doesn’t.
- To do such transformations, we could create a ConfigTarget object and assign it as the value of ‘#config_target’.
- The ConfigTarget constructor accepts 4 arguments.
- Config name (Required): The name of the config object being read from or written to, e.g. `my_custom_module.settings`.
- Property path (Required): The property path(s) being read or written, e.g., `username`
- fromConfig (Optional): A callback that transforms the value stored in the configuration before it gets displayed in the form.
- toConfig (Optional): A callback that transforms the user input in a field before it gets saved in the config.
- Lets us create the ConfigTarget object for our checkbox field. First let's create the callbacks
- If the user checks the checkbox, we need to store ‘YES! I WILL CONTINUE TO EXPLORE THE UNIVERSE’. Otherwise, we need to store ‘NO! I WILL DESTROY THE STONES, RETIRE, AND BECOME A FARMER’. So our toConfg callback can be written as
- Similarly,if the value present in the config is ‘YES! I WILL CONTINUE TO EXPLORE THE UNIVERSE’, the checkbox should be checked when the user views the form. Else checkbox should be unchecked. So most simply, our fromConfig callback can be written as
- So, the ‘future_plan’ field can be rewritten as:
- We need to update the schema file as well. Let us keep the type as ‘string’ since we are saving a string in config.
- Load the form again and try updating the checkbox. You will see the ‘future_plan’ key getting updated in config depending upon the state of the checkbox and vice versa.
![Configuration checkbox message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa6027005bf01fc8c6_AD_4nXcdI_paJBcdknfr7UYEI2I1eH1wubBacJ3URCAJ0LIBhjHmnYF9RSWPLWoxHTkhjPYyp48ZAtIW8ecavENe-ZJY8Yw0K3ONR48fLJSCzBRluJIMClUlsomSP-Sr2vl0dGb8deD8ttxfbo95dRhohSZ3mFN7.png)
Adding a custom validation constraint
- Adding a custom constraint involves 2 steps.
- Creating a validation constraint.
- Assigning the validation constraint to the field in the schema file
- Add a radio field to our form.
Since everyone loves Iron Man, let's add a custom validation constraint to force the user to select ‘Yes’ always.
- First, create SaveIronmanConstraint in my_custom_module/src/Plugin/Validation/Constraint/SaveIronmanConstraint.php and add the error message there.
- Then, create the ‘SaveIronmanConstraintValidator’ in my_custom_module/src/Plugin/Validation/Constraint/SaveIronmanConstraintValidator.php
- Finally,assign the id of our custom constraint to the ‘save_ironman’ field in the schema file.
- Load the form now and try to save it by selecting ‘Seems suspicious’.
![Error status message](https://cdn.prod.website-files.com/6470768de8327f36a7ae11a5/667960fa3b6afa08952bd0a9_AD_4nXdB_-D4s_1oVV8XuZSzgbh9MSR36vJXVtqvRFcIRAh_foyMAEUFzEkUzdl-RK3AIdPQgevxB2aPHoRgBcF3L1TUakh5fQ5z_GWLEDVbvY4m4IWygNEWpEC5GtJZCDZe2sNe3PchhboiGaUUleb3H0XyxTo.png)
You might feel skeptical about using the ‘#config_target’ property in config forms for multiple reasons. One of the reasons could be that when we add validations at the form level, all the logic would be under the same form class, which is convenient in most cases. However, validations added at the form level would only work when the user saves the form. In contrast, the new config validation would work in other scenarios, such as when applying recipes to the site, when saving configurations programmatically, etc. Many forms in Drupal core have already adopted the ‘#config_target’ approach to validation. It's an integral part of many ongoing and upcoming Drupal initiatives as well. So, adopting it is definitely a step forward that will keep our module ready for future Drupal releases.
A quick recap of all the things we explored in this blog
- The new ‘#config_target’ property and how to use it.
- Commonly used validation constraints (NotBlank, Choice, Regex etc.)
- Transforming the form values before saving to configuration and vice versa.
- Creating custom validation constraints.
- Advantages of the new configuration validation.