Support Entity Browser

Created on 15 April 2025, 4 days ago

Problem/Motivation

As we (at our company) have been increasing the usage of this module for composite fields, we found ourselves in need to be able to select entity references using Entity Browser β†’ , to keep setup consistency within projects.

Proposed resolution

Create submodule custom_field_entity_browser containing a field widget that uses Entity Browser.

✨ Feature request
Status

Active

Version

3.1

Component

Code

Created by

πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

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

Merge Requests

Comments & Activities

  • Issue created by @andreasderijcke
  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    andreasderijcke β†’ changed the visibility of the branch 3519258-support-entity-browser to hidden.

  • Pipeline finished with Success
    4 days ago
    Total: 751s
    #473895
  • Pipeline finished with Success
    4 days ago
    Total: 733s
    #473997
  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    Wow, thanks for all the work towards this. Let me test this out when I get a chance and see how it works and will certainly help get this supported as others have also mentioned wanting this feature. I appreciate the contribution :)

  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    It's a still work in progress!

    Currently struggling with getting the selection in the entity browser stored and the entities rendered in the widgets.

  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    It seems I was using 1.x branch of entity_browser as reference, so synced with the 2.13 release.

    Managed to get selection (working with fixed cardinality of 1) saved and loaded on reload of the node edit form. But the 'edit', 'replace', 'remove' buttons of the entity browser are not shown properly, must be conflict of their (wrapper) markup with the parent widget wrappers.
    That is on the list for tomorrow, as once fixed, it will be easier to fix and test the remaining gaps.

    I will continue to work on this the next days during the Drupal Dev Days in Belgium.

  • Pipeline finished with Success
    4 days ago
    Total: 558s
    #474204
  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    Sounds good. The isApplicable() should maybe check if there are actually any entity_browser entities and that select field should be required? I noticed also an entity_browser can have an upload widget but it would be irrelevant if the entity_reference target_type is not one of file||image. We don't need to check for the field_type otherwise in isApplicable() as its already defined in the plugin attributes. I havn't played much with entity_browser but i'm sure theres some settings that are irrelevant since we can only ever have 1 selection.

  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    The isApplicable() should maybe check if there are actually any entity_browser entities and that select field should be required

    At the moment, I favour to not do that, so you'll be able to see the widget and its options and be triggered to configure some entity browsers if you don't have any. Otherwise, you might be wondering why the widget isn't shown as an option.

    I noticed also an entity_browser can have an upload widget but it would be irrelevant if the entity_reference target_type is not one of file||image

    As far as I know/understand things, upload is handled by/in the entity browser modal and this widget is just to expect the entity ID in return.

    We don't need to check for the field_type otherwise in isApplicable() as its already defined in the plugin attributes.

    Ah, great!

    Related, entity_browser defines an adjusted widget for file and image. We might want to follow that, and keep this one to just 'entity_reference' (as you added file and image types)?

    ... i'm sure theres some settings that are irrelevant since we can only ever have 1 selection.

    Yes, very likely.

    As soon as the basic usage flow falls into place, I expect it will be more clear what pieces of can be scrapped.
    For now, I'm keeping things I don't understand/have not checked or adjusted yet, for easier comparison with the original.

    Related (again): I've heard there is some discussion going on about dropping support for submodules in contrib modules. (A necessity for project browser, also something related to composer/packaging issues, ...).
    So perhaps, since this quite a complicated widget, it might be opportune to not merge it in the main module but release it separate, which should also make it easier to develop the main module without being 'dragged down' by complicated extensions like this and manage their dependencies.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    Oh! I didn't realize the adjusted widget option. Okay, i'll strip those additional types back out. It sort of worked for an image upload when i was testing but resulted in error on edit so that may be why.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    I modified the logic a bit to get the actual saved value as it was erroring out.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    @andreasderijcke,

    The schema is all correct now and I fixed the remove button so its basically pretty functional now from my testing. I also removed the prefix with the cardinality message as its pretty irrelevant in that only 1 can ever be selected. The replace button doesn't show up... not sure if its supposed to and if its a bug with entity_browser module or what the logic is around that as maybe thats some sort of file/image thing only.

    I'll leave it to you to refine further but its in a pretty good functional state at least for now.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    Related (again): I've heard there is some discussion going on about dropping support for submodules in contrib modules. (A necessity for project browser, also something related to composer/packaging issues, ...).
    So perhaps, since this quite a complicated widget, it might be opportune to not merge it in the main module but release it separate, which should also make it easier to develop the main module without being 'dragged down' by complicated extensions like this and manage their dependencies.

    I havn't heard that. I get the reasoning but thats probably an unrealistic thing from happening anytime soon IMO to even worry about. Since we have other sub-modules... I'd just assume probably keep it the way it is unless there is any real substance to this idea actually happening in our lifetime ;)

  • Pipeline finished with Success
    3 days ago
    Total: 638s
    #474999
  • Pipeline finished with Success
    3 days ago
    Total: 590s
    #475009
  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    I'll leave it to you to refine further but its in a pretty good functional state at least for now.

    Thank you, that was of much help!

    I made the 'remove' button work, found an issue with the 'add another item' on the field itself + cleanup some code.

    It is very likely there is more code we can strip, and some specific situations we have to handle properly or different compared to the original.

    I realized for some reason the replace and edit icon are the same. When you disable the edit but enable the replace, the pencil icon still shows up and the replace functionality works on click. I don't know if we have something wrong in our implementation or if thats just the way it works... which would be odd

    That sounds like a (admin) theme compatibility issue. Which theme are you using?
    How does it look on a default entity reference field with the original entity browser widget?

    Using Claro, there are no icons and the labels are correct.

  • Pipeline finished with Success
    3 days ago
    Total: 751s
    #475230
  • Pipeline finished with Success
    3 days ago
    Total: 558s
    #475345
  • Pipeline finished with Success
    3 days ago
    Total: 546s
    #475355
  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    I was using Gin. I see what you mean now after switching to Claro which btw is a much clunkier UI with the buttons stacking like that. Gin provides inline icons which is way cleaner but its using the same pencil icon for edit and replace buttons with same positioning in the css so one is overlapping the other. It's pretty odd if that was intentional on their part. I'll have to look at trying to patch that for them. So... the buttons are there and functional, just a theming issue with Gin.

  • Pipeline finished with Success
    3 days ago
    #475369
  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    So, fixed other issue cause by the '$row_id' from the original that is replace here with the delta.
    After some playing around with removing, replacing of a selection within a field item, things seems to work as expected.

    Where we still do have issues, is when you start removing and adding new field items, you will see the new field item already prefilled with the selection previously stored for that delta.

    But, before trying to fix this, I think it might be best to simplify all the code related to determining and storing the selection, as we always will have either a single item (or none) per delta, so we can get rid of the all array_map usages, some loops etc (+ also apply more strict typing where possible etc).
    Once done, it might be easier to figure out how to deal with the remaining issues.

    What is your take on this?

    I was using Gin. I see what you mean now after switching to Claro which btw is a much clunkier UI with the buttons stacking like that.

    Yea, that is also an issue on the original widget in Claro.
    To make sure I understood you correctly, you're going to work on patch for Gin support for entity_browser module, right?

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    I will probably hold off on the patch for Gin. They are using a sprite for the icons and I'm thinking this might have been just some sort of intentional workaround as they don't have an icon for 'replace' so someone just did something hacky with css. It's not really anything that needs to us back.

    Nice on getting rid of the selection mode setting and I agree with you, simplifying to the absolute minimum we need is a good idea. Ajax is REALLY tricky with getting the right deltas and such. I've done alot of it in various other plugins. The media library widget was also very tricky to account for and then you start having to deal with different parent array structures with layout builder and paragraphs and its all the more complicated. Thats why in many places I've ended up just building my own widgets from scratch and doing it my way instead of trying to integrate with other modules where their code is generally unfinished. For instance my versions of viewfield & hierarchical_select both work correctly for multi-values and within paragraphs & layout builder. I can't say the same for the contrib module versions of those. Not trying to be snarky or arrogant or anything.... I'm just a little anal about things not being put out in the wild in broken state.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    I ultimately need to expand the unit testing to cover all these various widgets and formatters. Writing those is not something I'm really into. Hopefully as more people use this module, someone will volunteer to help out in these areas. And... if you have any interest in being a maintainer officially for custom_field, just say the word! I welcome and appreciate the help as its alot on a single person ;)

  • Pipeline finished with Success
    1 day ago
    Total: 594s
    #476177
  • Pipeline finished with Success
    1 day ago
    Total: 645s
    #476189
  • Pipeline finished with Success
    1 day ago
    Total: 632s
    #476222
  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    So,

    • Removed the code dealing with multiple items within a field item
    • Added the best solution/workaround I could piece together right now, for the add more/remove field items and the form state stored IDs issue
    • Some refactoring for more strict typing
    • Just the getEntityByTargetId() I'm still wondering about if we do need to keep it. It is used, but I do not yet have a clear picture on when
    • Added some ignore patterns of errors we can't fix any time soon (as far as my experience goes) to phpstan.neon but commented out, as those errors only appear from level 6. Getting my code pass level 7 with only unfixable errors ignored, is one my current personal goals.

    Feel free to add me as co-maintainer!
    Happy to help.

    About writing tests, I've made some in the past, but it's steep mountain to climb every time again.
    But having specific cases, is a good way to start again.

  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent

    I've tested the current status in the project where the need orginated, with Media types for images, remote video,... it all worked as expected.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    It seems to work fine in normal nodes however I found an issue with a custom_field attached to paragraphs.

    Steps to reproduce:

    1. Create a paragraph type and add a custom_field (can clone the config from any other field)
    2. Set the reference settings and entity browser widget
    3. Set the custom_field cardinality on paragraph to unlimited
    4. Add a paragraph field to a node and set that new paragraph type.
    5. Set the the paragraph field cardinality to unlimited.
    6. Create a node
    7. Add a paragraph, add multiple custom fields selecting different entities with entity_browser widget each time (this works)
    8. PROBLEM - Add a second paragraph, notice the custom_field reference field already has default value from first paragraph > first custom_field > reference (this is wrong).

    I think this is gonna be an issue around the parents array and deltas similar to the media library widget that I had challenges with but eventually sorted out. It can get even trickier when there is paragraphs within paragraphs (ughh... please never) but my solution for media_library worked there too.

  • πŸ‡ΊπŸ‡ΈUnited States apmsooner

    Here there is a hardcoded field_name so that won't work...

    // @todo Figure out a better way to enforce a 'clean start' when adding
        // new field item for whose delta a value already may have been stored
        // in the form state (below), as the field item 'remove' button does not
        // exist when building the widget element, so we cannot attach
        // 'removeItemSubmit' to it.
        $triggering_element = $form_state->getTriggeringElement();
        if (
          is_array($triggering_element)
          && array_key_exists('#name', $triggering_element)
          && $triggering_element['#name'] === 'field_articles_add_more'
        ) {
    
          // If the delta is not present in the user input, it means it is new or is
          // being added again. In both cases, we want to start with an empty
          // selection.
          // By forcing $entity to NULL, a selection stored in the form state (if
          // any) will also be set to NULL hereafter.
          $user_input = $form_state->getUserInput();
          if (!NestedArray::keyExists($user_input, [$field_name, $delta])) {
            $entity = NULL;
          }
        }

    I think we will need to set a variable through like $parents = $form['#parents'] that would get evaluated in the getFormStateKey(). It's normally gonna be empty except when the field is attached to paragraphs. You can ksm($parents) and see for yourself. Ultimately the $form_state_key is wrong when paragraphs are involved as its returning something like this and not accounting for the actual field containing the paragraphs:

    array:2 [
      0 => "entity_browser_widget"
      1 => "paragraph::field_testing:0"
    ]

    In my case i have a field named 'field_paragraphs' on the node so itself would have a delta and the field attached to it also has its own delta so not sure if this is an issue also with the widget on regular entity_reference fields also or if we can workaround it somehow. It's complicated... See Media library widget for similar: https://git.drupalcode.org/project/custom_field/-/blob/3.1.x/modules/cus...

  • πŸ‡§πŸ‡ͺBelgium andreasderijcke Antwerpen / Gent
Production build 0.71.5 2024