Problem/Motivation
The form for a simple config has an element structure that is closely related to the config schema of that config. The config schema says what type of data each item is, and gives labels.
Saving form values into config is pretty much automatic, with the '#config_target' property and if the validation is done at the config level.
If we put all these things together, it should be possible to generate most of the form structure for simple config.
Things like option values would still need to be provided in the form.
For example, with LoggingForm, everything except the array in '#options' could be generated from the config schema.
Steps to reproduce
Proposed resolution
Provide helper code in ConfigFormBase which automatically generates a form from the config schema.
The buildForm() method can call this, and then make additional changes. E.g.
public function buildForm(array $form, FormStateInterface $form_state) {
$form = $this->buildFormForConfig('system.logging');
// The option labels aren't defined in the config schema, so we need to add them here.
$form['error_level']['#options'] = // yada yada
}
This would be a big simplification for most forms that are for plain config (i.e. not a config entity).
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
Issue fork drupal-3477363
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
joachim commentedI've already built something similar for Module Builder, which automatically builds a FormAPI array from a data structure. So I could adapt that code.
What I'm stuck on is how to get the config schema for a particular config name.
Comment #3
joachim commentedComment #4
jonathanshaw+1 for the idea.
Maybe that's a limitation of the current schema that could be improved down the road.
Comment #5
joachim commented> What I'm stuck on is how to get the config schema for a particular config name.
@james.williams pointed me in the right direction in Slack for how to do this -- please add credit for this!
> Maybe that's a limitation of the current schema that could be improved down the road.
Yup, having option values and labels would be necessary if we want to be able to make config forms based on a schema retrieved over REST which AFAIK is a goal.
Comment #6
james.williamsHi, thanks Joachim. I've written up what I was up to https://www.computerminds.co.uk/articles/automatically-generate-forms-co... which was for a configurable block plugin in which I explored generating as much of the form as possible from the config schema. I've posted my code up at https://gist.github.com/anotherjames/bcb7ba55ec56359240b26d322fe2f5a5 . Here are a few thoughts from my early experiments with this...
My case was a configurable plugin form, whereas the original suggestion here is for forms extending ConfigFormBase. Can we help both cases, and maybe more? There is code currently in config_translation for automatically generating forms from config schema, such as
config_translation_config_schema_info_alter()and the parts ofConfigTranslationFormBase::buildForm()that fetch the schema and build form elements. So maybe the first step to achieve this feature request is to identify and refactor those parts out of the config_translation module to become more useful in other cases (including monolingual sites!). Of course having a concrete use case (like the suggestion for ConfigFormBase) might help pull that possibility along, and then facilitating other cases could be added.The config_translation approach uses a 'form_element_class' key in the schema; what other keys specific to this could be supported usefully? In my example, I added 'default' (which I then used in my plugin's
::defaultConfiguration()method) and 'locked' (for#disabledon the form elements). Maybe that's just scope creep, but it hints at the possibility of shifting the inherent definition of config data into the schema rather than form API.Not necessarily! If a data item only has certain allowed values, those can (should?) be defined as constraints on it - e.g. using the Choice constraint (and/or Enums?!). Then the form element could be automatically generated with its #options set from that. There will always be cases where it makes sense for a form element to be customised further (e.g. for things like #empty_option or #placeholder) which really are only about the form, not the inherent definition of the data. But that just means a form making use of this could just merge in those changes after calling the parent/trait method implementation that does the automatic form building.
Thanks for opening this issue and building some momentum around this idea! I thought maybe for now I was just on my own crazy island exploring this idea ;-)
Comment #7
andyf commented+1
Related: #2949888: Enhance config schema for richer default experiences
Comment #8
jonathanshawFrom @wimleers #2949888-24: Enhance config schema for richer default experiences:
Comment #9
joachim commented> Already in core — see this for ckeditor5.plugin.ckeditor5_language for example:
Yes, but that's only the values. It doesn't have the option labels.
I'm making progress on this.
Comment #10
murzThere are several contrib modules that allow building configuration forms directly from the schema:
They work very good and significantly decrease the amount of boilerplate that needs to be written!
But the main issue is the strict format of the Drupal Schema format that misses the "description" property, which is crucial for most of the forms to provide good UI.
As a workaround, we can use the
third_party_settingsproperty that is already widely used in different parts of Drupal, so I created a feature request to add this property: #3522197: Allow third_party_settings property in the Drupal Schema format to allow storing metadata and custom module-related data - please join ;-)So, maybe we can reuse the approach from one of those modules in Drupal Core?
Comment #11
joachim commentedI keep forgetting to push the work I did on this so far.
I converted a few of the core settings forms, and found that in most cases, the form's structure is rather different from the structure of the config, which requires some tweaking of the automatic form code. The Views settings form is particularly bad for this!
Comment #12
joachim commentedPushed my WIP branch to a fork -- sandbox-3477363-automatic-config-forms
Comment #13
murzFaced the same issue, and I believe that extending the schemas with the form related metadata is the best way to resolve this issue.
So, by default - all configs will be rendered as they described, and some of them look great, for example - 'system.performance'.
But if you need to customize the form for a bad-looking config, you can put additional metadata directly to the schema, describing the required form design.
This approach with storing metadata in the "third_party_settings" property I use in the module Schema Form to render the forms directly from the schema, without any custom PHP, an example:
What do you think about this approach?
Comment #15
joachim commentedHmm I'm not keen on this -- it's overloading the schema with things that go beyond its designed intention, which is to declare the structure and format of config. I can see a case for adding a 'description' key to go alongside 'label', but things like weight and classes are too much I think.
Comment #16
james.williamsThanks @murz for these contributions! Those modules look useful - I can see myself trying your schema_form one out for real, the next time I want to try generating forms from schema.
That said, I agree with @joachim's last comment. The schema should only really declare things that are inherent about the structure/definition of config. That's enough to generate the necessary stuff for form elements. But if the form wants to adjust things which are only really about the form element (especially the visual presentation, for example), the form class can still do so after the basic form elements have been generated (e.g. setting weights & attributes after the calls to
buildFormForConfig()in your MR implementation) :The line about what is "inherent about the structure/definition of config" might be slightly fuzzy though. Whilst weight & element classes are more clearly only for the form element, the approach I've taken added 'locked' and 'default' config keys which I mapped to
#disabledand#default_valuerespectively in the generated elements. These could be used beyond just the form elements, but could also been seen as overloading the schema to "go beyond its designed intention". I guess we'll need to find that line at some point - either as we explore solutions, or up front?The description is an interesting case - it seems a no-brainer to me, to support descriptions living in the schema - but not all existing
#descriptionstrings will be specifically about the config definition. Some will be be about how to correctly enter the value in the widget offered by the UI - which I don't think should live in the schema. In those cases, I would expect the form to override / add to any description coming from the schema.Comment #17
joachim commented> added 'locked' and 'default' config keys which I mapped to #disabled and #default_value
'locked' makes sense to have in schema too, yes.
But for 'default', surely the default value is already in config/install, so adding it to the schema violates DRY.
> Some will be be about how to correctly enter the value in the widget offered by the UI - which I don't think should live in the schema. In those cases, I would expect the form to override / add to any description coming from the schema.
Yup, agreed.
Comment #18
james.williamsThe 'default' can be useful for things like plugin configuration or config entities that won't ship in /config/install . (e.g. The gist I linked to is for a block plugin which populates its implementation of
defaultConfiguration()from the schema, not justblockForm().)I'm quite happy to park supporting specific keys like these though; let's get as far as we can without having to add too much to schema definitions or the TypedConfigManager, etc! Each possible addition could be its own separate follow-up to be considered on its own merits, without having to block progress on just being able to automatically generate form elements. Description is the only one I think we definitely agree on.
Comment #19
joachim commented> The 'default' can be useful for things like plugin configuration or config entities that won't ship in /config/install
Default values for plugins are in the plugin class's defaultSettings() method, and default values for config entities are the default values of the class properties.
Comment #20
murzThank you for your comments, agree that all form rendering-related metadata is not good to store in the schema of the data. But we have to store it somewhere, right? ;) Storing in the PHP code looks not very good (for basic cases), it's better to have the declaration format, not the calculating format for them.
So maybe declare a separate YAML file that will store formatting for the schema data?
And about including description into the allowed properties of the schema - let's give it a go? Actually, I tried to find the source where the allowed values for Drupal Schema come from, but still found nothing, only an external file https://json.schemastore.org/drupal-config.json
About the idea of my module Schema Form - the main idea is to declare all the data to render the form in the YAML format. This should be not specifically the Drupal Schema YAML file, but any separate file, like
my_data.form.yml, where you can declare the data source (config_object name or any Drupal Schema name) and extend it with form-related data (more detailed descriptions, weight, design elements, state dependencies, conditions, etc).Also, it should be a good big step for decoupling the admin interface from the PHP rendering, to allow exposing form structure using GraphQL and rendering config and entity edit forms using a decoupled approach (in React, Vue, Web Components, etc).
Comment #21
james.williamsWe currently 'store' the properties in the form API code. Which, sure, is not declarative, but it's a far bigger issue to suggest replacing shifting form API to truly declarative code. Personally, I'm still in favour of keeping form-specific stuff with the form, rather than in the config schema.
How do we move forward with this if we don't quite agree? What about my suggestion to progress with only the smallest addition to the schema keys (description), for now? Then maybe others could be added later only if & when there's agreement? (e.g. form-specific keys, 'default', etc.) I don't see any need for those parts to hold up the wider progress.
---
Unrelated: I noticed today that the layout_paragraphs module is storing its config descriptions in its config schema:
Element in form class
Config object schema
Nice to see this idea gaining traction, probably under its own steam, elsewhere!
Comment #22
joachim commented> We currently 'store' the properties in the form API code. Which, sure, is not declarative, but it's a far bigger issue to suggest replacing shifting form API to truly declarative code. Personally, I'm still in favour of keeping form-specific stuff with the form, rather than in the config schema.
Yeah, that was my feeling too -- FormAPI is already a structure that defines the form, it feels weird to start building a second kind of structure for it.
> What about my suggestion to progress with only the smallest addition to the schema keys (description), for now?
Sounds good to me. Add description for now, and pull the default value from where it's defined.
Comment #24
murzThank you for sharing your thoughts, I added a separate issue to add the "description" field to the list of allowed in the schema files: #3580701: Add "description" property to the Drupal config object (schema.yml file schema)
Comment #25
murzAnd who is interested in this topic, I will present the Schema Form module at the DrupalCon Chicago 2026 - here is the speech page https://events.drupal.org/chicago2026/session/stop-creating-drupal-forms... - who will attend there, come to listen and discuss! Will try to move forward with adding this feature to the Drupal Core directly.