Problem/Motivation

In #2656196: Initialize Entity Browser with existing selection we added support for passing existing selection to the entity browser. This is one of the follow-ups.

Proposed resolution

Update field widgets that we provide to be able to laverage the new possibilities. This feature should be configurable. We will have two modes:

  • Append mode: Existing selection is not passed in and entities returned from the Entity browser are appended to the current selection. This is current behaviour.
  • Edit mode: Existing selection is passed in and entities returned from the Entity browser replace existing selection. In this mode we allow user to edit entire selection in context of the entity browser.

There is some code in the original patch of the parent issue (uploaded by @webflo), that should be used when implementing that. @webflo should be attributed in the commit message for this issue as a result of that.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

slashrsm created an issue. See original summary.

mtodor’s picture

@slashrsm - I have started working on original issue, now I want to finalize changes and prepare patches, but I have one problem and I'm not sure, should I go in that direction.

I have created options for Append Mode (it's actually Combine Mode) and Edit Mode. Now open question is, should we allow multiple selections of same entity or not? In general, that functionality should be supported, but should I make it with this patch (ticket) or not?

slashrsm’s picture

Yes, I think that we shouldn't make any assumptions about that. Entity reference field supports that and we should too in my opinion.

slashrsm’s picture

mtodor’s picture

Status: Active » Needs review
FileSize
23.85 KB

Here is patch, unfortunately I wasn't able to split it in tow parts for #2767739: Define which selection displays support existing selection and which not, so basically changes relevant for that ticket are in this patch.

Implemented things relevant for #2767739: Define which selection displays support existing selection and which not:
- annotation for selection displays (are they allow preselection or not)
- using of annotation in field widget to check is preselection supported or not

Implemented things relevant for this ticket:
- selection mode option for entity reference field widget (settings + summary)
- propagate selection to selection display if edit mode is used

Additional things fixed are:
- multiple selections of same entity (also adjusted remove functionality to work properly for that in multi step selection display and selection display for field)
- appending of selected entities (before it would combine selection from entity browser with existing selection), now it will be appended
- generation of weights didn't work properly in multi step selection display
- test is adjusted to use multi step selection display and edit selection mode

After these changes, it's possible to edit/remove/add selection with multi step selection display.

Status: Needs review » Needs work

The last submitted patch, 5: allow_passing_existing-2767737-5.patch, failed testing.

The last submitted patch, 5: allow_passing_existing-2767737-5.patch, failed testing.

mtodor’s picture

Status: Needs work » Needs review
FileSize
23.65 KB

Updated patch, a bit of restructure and defining default selection mode for field.

slashrsm’s picture

Status: Needs review » Needs work

Thanks! This looks great! First review:

  1. +++ b/js/entity_browser.common.js
    @@ -21,49 +21,44 @@
    +    var entity_reference_options = drupalSettings['entity_browser'][uuid];
    

    This is common code, which can be used outside of the field scope. We shouldn't refer to the entity reference.

    I am personally a bit against this kind of assignments as I think they decrease readability of code.

  2. +++ b/js/entity_browser.common.js
    @@ -21,49 +21,44 @@
    +    // In case that edit selection mode is used,
    +    // overwrite of selection will be made.
    

    Comment should use all 80 characters.

  3. +++ b/src/Annotation/EntityBrowserSelectionDisplay.php
    @@ -40,4 +40,11 @@ class EntityBrowserSelectionDisplay extends Plugin {
    +   * Information does Selection Display support preselection.
    

    This comment reads a bit strange.

  4. +++ b/src/Element/EntityBrowserElement.php
    @@ -62,11 +98,25 @@ class EntityBrowserElement extends FormElement {
    +      // Check does Entity Browser Selection Display supports preselection.
    +      $entity_browser_selection_display_definition = $entity_browser->getSelectionDisplay()
    +        ->getPluginDefinition();
    +
    +      if (!$entity_browser_selection_display_definition['acceptPreselection']) {
    

    It would probably make sense to have a helper function to check that. Would improve DX in my opinion.

  5. +++ b/src/Plugin/EntityBrowser/SelectionDisplay/MultiStepDisplay.php
    @@ -186,9 +190,11 @@ class MultiStepDisplay extends SelectionDisplayBase {
    +      $diff_selected_size = count($selected_entities) - count($weights);
    +      if ($diff_selected_size > 0) {
    

    Do we need this extra variable?

  6. +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php
    @@ -325,8 +341,14 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
    +      $stored_ids = $form_state->get(['entity_browser_widget', $this->getFormStateKey($items)]);
    +      $indexed_entities = $entity_storage->loadMultiple($stored_ids);
    +
    +      foreach ($stored_ids as $entity_id) {
    +        if (isset($indexed_entities[$entity_id])) {
    +          $entities[] = $indexed_entities[$entity_id];
    +        }
    +      }
    

    We load entities and then we iterate over their initial ids. Seems redundant. What am I missing?

  7. +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php
    @@ -381,11 +412,18 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
    +    $selection_mode = $this->getSetting('field_widget_selection_mode');
    +    if (
    +      $cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
    +      || count($ids) < $cardinality
    +      || $selection_mode == EntityBrowserElement::SELECTION_MODE_EDIT
    +    ) {
    

    We need to figure out how to refactor this condition. Probably with some logic before the actual diff.

  8. +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php
    @@ -381,11 +412,18 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
    +        '#default_value' => $entities,
    

    #default_value should only be set if edit mode is active.

  9. +++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php
    @@ -472,9 +511,10 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
    +        function ($item, $index) use ($id, $row_id) {
    +          return ($item != $id) || ($index != $row_id);
    +        },
    +        ARRAY_FILTER_USE_BOTH
    

    This will raise PHP requirement to 5.6 (which I am fine with). Core still depends on >= 5.5.9. We will need to document this requirement in README and check it in hook_requirements() if we want to do this right.

  10. +++ b/tests/modules/entity_browser_test/config/install/entity_browser.browser.test_entity_browser_iframe.yml
    @@ -9,8 +9,13 @@ display_configuration:
    -selection_display: no_display
    -selection_display_configuration: {  }
    +selection_display: multi_step_display
    +selection_display_configuration:
    +  entity_type: file
    +  display: label
    +  display_settings: {  }
    +  select_text: 'Use selected'
    +  selection_hidden: false
    
    +++ b/tests/modules/entity_browser_test/src/Form/FormElementTest.php
    @@ -28,6 +28,7 @@ class FormElementTest extends FormBase {
    +      '#selection_mode' => EntityBrowserElement::SELECTION_MODE_EDIT,
    

    Do we need to update those since we're not actually testing this?

  11. There are quite some changes in the field widget class. In the past we've seen a lot of bugs in the area of nested widgets (Paragraphs, Inline entity form). We need to thoroughly test that to be really sure that we didn't introduce any regressions.
mtodor’s picture

Status: Needs work » Needs review
FileSize
33.99 KB

Here is new patch, no interdiff, because it's based on new code.

  1. I have removed new variable and returned drupalSettings usage.
  2. Adjusted
  3. Adjusted. It should be better now.
  4. Adjusted
  5. I think it's better to have new variable, because it makes code more readable and it's also used in for loop.
  6. This is problem with multiple same entities selected. Method loadMultiple() will return unique list and that's not correct. (fe. entities with following IDs are selected: 1, 2, 1 -> loadMultiple(), will return only 2 entities (1, 2) and we actually have 3.). Maybe there is some better way to get same result? And also $entities should be plain array, not associative.
  7. I have moved check into EntityBrowserElement. I think that logic related to EB functioning should be kept in that class, that will make DX a bit better. (I guess)
  8. I think that logic about propagating selected entities to entity browser should be responsibility of form element, not widget. Because passing of configuration for EB is handled by form element.
  9. I have changed it to support 5.5.9. In this case we don't need to use array_filter, simple foreach is sufficient.
  10. Problem in this case was: test checked preselection and that can't work anymore without "Edit" selection mode. To have edit selection mode working, selection display that supports preselection have to be used and that's multi step display. In other case I could remove part of test, but instead I have adjusted test, so that preselection is still checked. I have changed it from last patch, to pass selection mode as query parameter. Selection display have to be multi step and I think it doesn't affect other tested cases.
  11. Yes, we need way more tests.

  
Additional changes:

  1. constant CARDINALITY_UNLIMITED is removed from EntityBrowserElement, because it's already defined in FieldStorageDefinitionInterface and that one is used in other parts of code. So there is no need to define new one.
  2. few code sniffer fixes: wrapped '#description' with t() in View selection display, code styles, etc.

Status: Needs review » Needs work

The last submitted patch, 10: allow_passing_existing-2767737-10.patch, failed testing.

slashrsm’s picture

This looks almost ready. Thank you for your hard work! Few nitpiks:

  1. +++ b/src/Element/EntityBrowserElement.php
    @@ -33,9 +36,35 @@ use Drupal\Core\Entity\EntityInterface;
    +   * Selection from entity browser will be prepend to existing list.
    

    s/prepend/prepended

  2. +++ b/src/Element/EntityBrowserElement.php
    @@ -33,9 +36,35 @@ use Drupal\Core\Entity\EntityInterface;
    +   * When this selection mode is sued, then entity browser will be populated
    +   * with existing selection and returned selected list will replace existing
    +   * selection. Also used selection display has to support preselection.
    

    s/sued/used (noticed by @primsi :))

    Last sentence reads a bit strange.

  3. +++ b/src/Element/EntityBrowserElement.php
    @@ -33,9 +36,35 @@ use Drupal\Core\Entity\EntityInterface;
    -  const CARDINALITY_UNLIMITED = -1;
    +  const SELECTION_MODE_EDIT = 'selection_edit';
    

    Cardinality constant was put here on purpose. The reason for that was the fact that the Entity browser doesn't depend on fields at all. Fields are just one possible usage of it.

    However, this doesn't seem that important. While I prefer the current solution I am open for other solutions too.

  4. +++ b/src/Element/EntityBrowserElement.php
    @@ -45,16 +74,76 @@ class EntityBrowserElement extends FormElement {
    +  protected static function checkPreselectionSupport(EntityBrowserInterface $entity_browser) {
    

    I think that this function belongs to the Selection display plugin class.

  5. Ad 6: Understood. Let's add a comment that explains that.
  6. Ad 8: Good point. Docs for #default_value on EntityBrowserElement are not correct in that case. Let's update them.
mtodor’s picture

Status: Needs work » Needs review
FileSize
36.58 KB
8.91 KB

I wasn't able to create proper interdiff (it's based on code before last 2 commits).

  1. Fixed
  2. I hope so it's better now. I don't know how to formulate it better. :)
  3. I prefer option with defined constant in EntityBrowserElement, but I don't like mixing of constants. So my proposal is to write note in comment for constant in EntityBrowserElement and use it in rest of code. Cleanest option would be to map unlimited cardinality in field widget, but that would just add unnecessary complexity.
  4. Yes. It fits there perfectly.
  5. I have added comment, I hope so it's clear enough.
  6. Comment adjusted.

  • slashrsm committed 7923754 on 8.x-1.x authored by mtodor
    Issue #2767737 by mtodor, webflo, slashrsm: Allow passing existing...

slashrsm credited webflo.

slashrsm’s picture

Status: Needs review » Fixed

Committed. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.