Field Storage for UI Patterns Configuration

Created on 4 March 2025, 29 days ago

Problem/Motivation

It should be able to store ui patterns inside the entity form.

Solution

Add an own storage including field widget.

πŸ“Œ Task
Status

Active

Version

2.0

Component

Code

Created by

πŸ‡©πŸ‡ͺGermany Christian.wiedemann

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @Christian.wiedemann
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    we also had some discussions about this, covering other different use cases from the contrib space.
    it appears, that would be nice, in the general case, to store not 1 ui_patterns configuration, but a sequence of n configurations (that covers the case of 1), and also being able to store other metadata in addition to the ui_patterns config (like a key/value).
    What do you think?

  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    Lets discuss this! I was just creating a simple field type to store ui patterns configuration.

  • Pipeline finished with Failed
    29 days ago
    Total: 566s
    #440252
  • πŸ‡«πŸ‡·France pdureau Paris

    it appears, that would be nice, in the general case, to store not 1 ui_patterns configuration, but a sequence of n configurations (that covers the case of 1), and also being able to store other metadata in addition to the ui_patterns config (like a key/value).

    Indeed, because we are talking about storage, which is hard to change once implemented, we need to do it right.

    Instead of storing directly the data from ComponentForm:

    component_id: 'ui_suite_bootstrap:accordion_item'
    variant_id: null
    slots:
      title:
        sources: []
    props: {  }

    It will be better to be positioned one level upper and store a slot sources list:

    - component_id: 'ui_suite_bootstrap:accordion_item'
      variant_id: null
      slots:
        title:
          sources: []
      props: {  }

    This upper level will not be used yet, so:

    • this MR's field widget will only load the first item of the root list, check if it is a component structure, and build ComponentForm from the data

    For the field formatter, we can do event better, by updating the logic directly in ComponentElementBuilder::build(): add a new public method which is wrapping protected function buildSlot(array $build, string $slot_id, array $definition, array $configuration, array $contexts): array; or something like that, as an additional builder starting point.

    The goal is to avoid the need to create fake components like that when we want to build a list of slot sources instead of a single component:

    [
          '#type' => 'component',
          '#component' => 'my_project:fake_root',
          '#ui_patterns' => [
            'component_id' => 'my_project:fake_root',
            'slots' => [
              'content' => [
                'sources' => $data,
              ],
            ],
          ],
        ];
    
  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    Lets discuss that in a meeting.

  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    I think everybody have differnt use cases in mind. We should merge our ideas.

  • πŸ‡«πŸ‡·France pdureau Paris
    I think everybody have differnt use cases in mind. We should merge our ideas.

    Indeed. Different use cases doesn't necessary mean different implemntations.

  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    Yes! So from my point of view the Storage should store one complete UI Patterns config. We should change the cardinality to unlimited. But the content is just one config.
    What we are missing is a mechanism to change a config structure in whole. For this I want to bring the idea from Mikkael (or what I understand) into play. Maybe we add processors to config.

    component_id: comp
    variant_id: variant
    processors:
      - fake_root_processor
    props: ...
    slots: --
    

    These processors can be configured during the mapping process. This can also help to reduce the mapping if a config field exists.
    Each is processor is ... of course ... a configurable plugin.

    So fake_root processors is for your case. For our case to automaticly merge the config from the field to resulting we use another processor. so we don't need a source just one processor.

  • πŸ‡«πŸ‡·France pdureau Paris

    Let's try to move fast on this issue.

    So, we agreed each field item must be a single "object" and if we want a collection of them we configure the field as multivalued.

    What would be this object ? I am proposing it to have at least 2 field properties:

    public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
      $properties['source_id'] = DataDefinition::create('string')
        ->setLabel(new TranslatableMarkup('ID'));
      $properties['source'] = MapDataDefinition::create()
        ->setLabel(new TranslatableMarkup('Data'));
      return $properties;
    }
    

    Mapping exactly the current UI Patterns 2 form data structure.

    For example, a component source:

    source_id: component
    source:
      component:
        component_id: 'ui_suite_bootstrap:accordion_item'
        variant_id: null
        slots: []
        props: {  }

    A block:

    source_id: block
    source:
      plugin_id: announce_block
    

    A block with configuration:

    source_id: block
    source:
      plugin_id: 'views_block:content_recent-block_1'
      'views_block:content_recent-block_1':
        id: 'views_block:content_recent-block_1'
        label: 'Recent content'
        label_display: ''
        provider: views
        views_label: 'Recent'
        items_per_page: '5'
    

    An entity field:

    source_id: entity_field
    source:
      derivable_context: 'field:node:article:status'
      'field:node:article:vid':
        value:
          add_more_button: ''
      'field:node:article:status':
        value:
          add_more_button: ''

    Fabien, Christian, Mikael, are you OK with those 2 first field properties? Which other properties do you need?

    These processors can be configured during the mapping process. This can also help to reduce the mapping if a config field exists.

    Interesting but it looks like a distinct issue, no?

  • Pipeline finished with Failed
    20 days ago
    Total: 851s
    #448142
  • Pipeline finished with Canceled
    20 days ago
    Total: 160s
    #448166
  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    So I renamed the classes and add source_id and source to the storage. The widget only supports component right now. I like it so far. In future, we can provide more generic widget for different source. A complete generic one doesn't make sense from my point of view because a lot of widgets make only sense if they are preconfigred like the right component ID. But a source for simple widgets would be also nice. What is still missing is a Source for this but lets do this in a differnt issue.

  • Pipeline finished with Failed
    20 days ago
    Total: 707s
    #448168
  • Pipeline finished with Failed
    20 days ago
    Total: 583s
    #448195
  • Pipeline finished with Success
    20 days ago
    Total: 789s
    #448225
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    i haven’t posted the updated code yet, i have a large update to this MR i. preparation

  • Pipeline finished with Success
    16 days ago
    Total: 942s
    #450655
  • Pipeline finished with Success
    16 days ago
    #450714
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    i have created another issue to push some fixes (currently in the MR of this issue) in another MR first.
    see πŸ› [2.0.3] Various bugfixes and edgecases when using sources outside of ui_patterns Active

  • Pipeline finished with Success
    15 days ago
    Total: 570s
    #451235
  • Pipeline finished with Failed
    15 days ago
    Total: 562s
    #451360
  • Pipeline finished with Failed
    15 days ago
    Total: 551s
    #451417
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    Christian, Fabien (@goz), and Pierre, would you review please?

    there may be a last private comment from Pierre, unaddressed :

    - why do we use datatype custom, isn't map enough?

  • Pipeline finished with Success
    15 days ago
    Total: 590s
    #451423
  • πŸ‡«πŸ‡·France goz

    Something is wrong with the last commits, my submission is not stored anymore in field. FYI, i also use last commit from https://www.drupal.org/i/3511027 β†’

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    All good without the component widget, because ui_patterns_ui will provide a better one with customized form.
    previous bugs may be fixed, please review :)

  • Pipeline finished with Success
    14 days ago
    Total: 584s
    #452566
  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    I am fine now. Lets merge. A UI Patterns UI Form Widget will follow soon.

  • πŸ‡«πŸ‡·France pdureau Paris

    Sorry to disturb that late, but do we really need a custom data type (Source) storing the data in JSON?

    There are already data type in Drupal Core for such data structure which are (theoretically, we need to check) more performant for decoding/encoding nested PHP arrays, like https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TypedData...

    XB is also using a custom data type based on JSON, but I guess they send the exact data as HTTP Response Body, without the need of unserialize it to PHP arrays

    What do you think about that?

  • πŸ‡«πŸ‡·France goz

    Sad to see widget disappear while it was working in previous commits :(

    I think you are right @pdureau, MapItem could do the job BUT we have to be sure it do the job without having to tweak it and it does not seem to be really used

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    sorry for the disappearance, Christian was a bit disturbed by that widget, and thought ui patterns ui could be better, which would be true but would clearly need additional configuration to create that form
    and apply it. Maybe we reintroduce?

    the custom data type is ok for me, but i will give a try to map, every data type needs to have a chance 🀣

  • πŸ‡«πŸ‡·France pdureau Paris

    but i will give a try to map, every data type needs to have a chance 🀣

    Thanks you so much. Can you do some performance tests? If they are more or less the same, let's avoid the introduction of a custom data type.

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    Back at it !
    i did the change, even better than expected.

    Also i re-introduced the component widget but with a way nicer default mode and i took care of previous comments from Christian about how props were filtered.
    now it's a quite super clean default mode for editors, when ui_patterns_ui is not enabled.

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    i forgot to mention that :
    - i added a kernel test : it allows to test that the field is working (accepting the value) and the new mapping source also.
    - i corrected a few sources that were not tagged as widgets.
    - as you can remark, i introduced a few new properties to our component render elements. It allows us better control on how the forms are rendered by default (wrap into details or not, headings or not, hide/show sources for props, only show some props). The opens an even nicer "API" for other modules when they embed the component related forms (component form, slot form, prop form)

  • Pipeline finished with Success
    14 days ago
    #452748
  • Pipeline finished with Success
    14 days ago
    Total: 752s
    #452788
  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    Should we rename only_props to props_filter in the widget?

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    Yes good idea

  • Pipeline finished with Success
    14 days ago
    Total: 564s
    #453054
  • πŸ‡«πŸ‡·France goz

    Thanks to put widget back.
    Glad to see we are moving to MapItem, good job !

    I still have an issue, but we are almost there !

    With my layout paragraph, i have a field storage for components (props & variant).
    When i create my paragraph, content is stored correctly.
    When i edit my paragraph, preview is good, but saving loose changes.

  • πŸ‡«πŸ‡·France pdureau Paris

    The 2 field widgets have similar and confusing labels:

    • "Component slot source" ('A field widget for a "component" source.'): the exact same label as the prop type?
    • "UI Patterns slot source" ('A field widget for an UI Patterns source.'): what is the difference with previous?
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    field type :

    "UI Patterns Source"

    field widget :

    - All Slot Sources (UI Patterns)
    - Components only (UI Patterns)

  • πŸ‡«πŸ‡·France pdureau Paris

    Great. Can you also change the plugin IDs accordingly?

  • Pipeline finished with Success
    13 days ago
    Total: 564s
    #453308
  • Pipeline finished with Canceled
    13 days ago
    Total: 82s
    #453426
  • Pipeline finished with Canceled
    13 days ago
    Total: 84s
    #453427
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    Guys, i corrected the wording, i hope it fits our needs and reached consensus.

    With @goz, we also investigate a strange behavior using layout paragraphs and we corrected a bug :)
    list_class in the field type.

    it may be ready to be merge right now...

  • Pipeline finished with Success
    13 days ago
    Total: 576s
    #453429
  • Pipeline finished with Success
    10 days ago
    #455556
Production build 0.71.5 2024