Как передать текущее выделение в окно выбора сущностей

8

Я использую Entity Browser (2.x-dev в Drupal 8) в качестве виджета формы для поля ссылки на сущность пользовательского объекта. Браузер сущностей настроен

  • в качестве модального дисплея,
  • с одним виджетом,
  • и нет выбора дисплея,
  • используя вид с массовой выборкой браузера в качестве виджета, и
  • добавить выбранные объекты к текущему выбору ссылочного поля.

Выбор сущностей работает нормально. Но поле ссылки на сущность не должно иметь дубликатов.

Чтобы упростить выбор сущностей без дубликатов, я бы хотел отфильтровать уже выбранные сущности из результатов просмотра браузера сущностей. Таким образом, пользователи будут видеть только невыбранные объекты.

Для этой цели я создал пользовательский плагин arguments_default представлений, который предоставляет хранилище выбора обозревателя сущностей в качестве аргумента контекста по умолчанию для идентификатора сущности:

<?php

namespace Drupal\my_module\Plugin\views\argument_default;

use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * The entity browser selection argument default handler.
 *
 * @ViewsArgumentDefault(
 *   id = "entity_browser_selection",
 *   title = @Translation("Entity Browser Selection")
 * )
 */
class EntityBrowserSelection extends ArgumentDefaultPluginBase {

  /**
   * The selection storage.
   *
   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
   */
  protected $selectionStorage;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, KeyValueStoreExpirableInterface $selection_storage) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->selectionStorage = $selection_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_browser.selection_storage')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function access() {
    return $this->view->getDisplay()->pluginId === 'entity_browser';
  }

  /**
   * {@inheritdoc}
   */
  public function getArgument() {
    $argument = NULL;
    $current_request = $this->view->getRequest();

    // Check if the widget context is available.
    if ($current_request->query->has('uuid')) {
      $uuid = $current_request->query->get('uuid');
      if ($storage = $this->selectionStorage->get($uuid)) {
        if (!empty($storage['selected_entities'])) {
          $argument = $storage['selected_entities'];
        }
      }
    }
    return $argument;
  }

}

Проблема, с которой я сталкиваюсь, заключается в том, что текущий выбор в хранилище выбора всегда пуст, независимо от того, сколько объектов было выбрано в поле ссылки на объект, и даже после того, как я завершу модальное выделение и снова открою браузер объекта.

Что мне нужно сделать, чтобы текущий выбор отображался в хранилище выбора браузера сущностей?

Марио Стейниц
источник
подожди, значит ты имеешь в виду, что у тебя все готово выбрал (и сохранил) элемент, который ты не хочешь, чтобы этот элемент показывал в модале как выбираемый элемент? или вы имеете в виду не разрешать дублирование выбора на лету ... или вы имеете в виду, что вы хотите, чтобы уникальные данные по всему вашему контенту, если они были выделены, скрылись от просмотра?
taggartJ
Оба случая. Если объект является новым, и модальный тип используется для выбора связанных объектов, то сделанный выбор уже должен быть отфильтрован из браузера объектов, после повторного нажатия кнопки «выбрать» (до сохранения объекта). И, конечно же, после того, как он был сохранен и собирается быть отредактирован, текущий выбор ( #default_value) тоже должен рассматриваться как фильтр.
Марио Штейниц

Ответы:

4

Entity Browser не передает текущее поле значений по умолчанию в постоянных данных, но его легко добавить.

1) Добавить постоянные данные, используя field_widget_form_alter ()

/**
 * Implements hook_field_widget_form_alter().
 */
function mymodule_field_widget_form_alter(&$element, FormStateInterface &$form_state, $context) {
  if (!empty($element['entity_browser'])) {
    $default_value =  $element['entity_browser']['#default_value'];
    $ids = [];
    foreach ($default_value as $entity) {
      $ids[] = $entity->id();
    }
    $element['entity_browser']['#widget_context']['current_ids'] = implode('+', $ids);
  }
}

2) Обновите свой выбор так, чтобы, если пусто, он показывал все:

  /**
   * {@inheritdoc}
   */
  public function getArgument() {
    $argument = NULL;
    $current_request = $this->view->getRequest();

    // Check if the widget context is available.
    if ($current_request->query->has('uuid')) {
      $uuid = $current_request->query->get('uuid');
      if ($storage = $this->selectionStorage->get($uuid)) {
        if (!empty($storage['widget_context']['current_ids'])) {
          $argument = $storage['widget_context']['current_ids'];
        }
        else {
          $argument = 'all';
        }
      }
    }
    return $argument;
  }

3) Убедитесь, что у вас выбрано «исключить» и «разрешить несколько».

введите описание изображения здесь

Кстати, если вы обновитесь до последней версии объекта entity_browser, вам не понадобится ваш собственный плагин. Существует новый плагин представления значений по умолчанию entity_browser_widget_context, который можно настраивать.

Я также добавил проблему в очередь entity_browser, чтобы добавить эту информацию, когда она находится в widget_context.

oknate
источник
2

Я использовал ваш класс аргументов по умолчанию и немного отладил. Это мой подход:

Виджет браузера сущности сохраняет выбранные значения в своем currentсвойстве, которое заполняется, когда форма сущности открывается с существующей сущностью / выбором. Виджет также использует AJAX, когда модальное окно закрывается и currentсвойство обновляется соответствующим образом.

Таким образом, вы можете получить идентификаторы выбранной сущности, используя что-то вроде следующего в вашей форме / форме:

use Drupal\Core\Render\Element;

// Current selection. Replace 'field_references' with the actual
// name of your field.
$selection = [];
if (isset($form['field_references']['widget']['current'])) {
  $current = $form['time_records']['widget']['current'];
  foreach (Element::children($current) as $key) {
    if (isset($current[$key]['target_id']['#value'])) {
      $selection[] = $current[$key]['target_id']['#value'];
    }
  }
}

Другое свойство виджета, доступное в форме, является контекстом виджета используемого браузера сущностей. Вы можете просто добавить текущий выбор в контекст виджета и использовать эту информацию с аргументом по умолчанию для своих представлений (контекст виджета обновляется в хранилище выбора при каждой перезагрузке AJAX виджета / формы):

$form['field_references']['widget']['entity_browser']['#widget_context']['current_selection'] = $selection;

Затем измените ваш EntityBrowserSelection::getArgument():

  /**
   * {@inheritdoc}
   */
  public function getArgument() {
    $argument = NULL;
    $current_request = $this->view->getRequest();

    // Check if the widget context is available.
    if ($current_request->query->has('uuid')) {
      $uuid = $current_request->query->get('uuid');
      if ($storage = $this->selectionStorage->get($uuid)) {
        if (!empty($storage['widget_context']['current_selection'])) {
          $selection = $storage['widget_context']['current_selection'];
          if (is_string($selection)) {
            $argument = $selection;
          }
          elseif (is_array($selection)) {
            $non_scalar = array_filter($selection, function ($item) {
              return !is_scalar($item);
            });
            if (empty($non_scalar)) {
              // Replace the ',' with '+', if you like to have an
              // OR filter rather than an AND filter.
              $argument = implode(',', $selection);
            }
          }
        }
      }
    }
    return $argument;
  }

Благодаря этим изменениям я смог отфильтровать выбранные элементы из моего представления с помощью контекстного фильтра для идентификаторов объектов, выбрав

  • Когда фильтр недоступен: укажите значение по умолчанию, введите «Выбор браузера сущностей»
  • Больше: Исключить

Надеюсь, поможет!

Андреас Уайлах
источник
0

Я не мог заставить работать фильтр по умолчанию, но у меня был некоторый успех, выполнив следующую страшность:

function mymodule_views_pre_render(\Drupal\views\ViewExecutable $view) {
  if ($view->id() == "media_entity_browser" && $view->current_display ==
    'entity_browser_1') {
    $request = \Drupal::request();
    $prams = $request->query->all();
    $is_edit = FALSE;
    if (!empty($prams['original_path'])) {
      // testing with "/node/1/edit"
      $check_explode = explode('/', $prams['original_path']);
      if (in_array('edit', $check_explode)) {
        $edit_key = array_search ( 'edit', $check_explode);
        $entity_id_key = $edit_key - 1;
        $entity_id = $check_explode[$entity_id_key];
        $entity_type_key = $edit_key - 2;
        $entity_type = $check_explode[$entity_type_key];
        $selected_ids = [];
        try {
          $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
          // This sucks bacause field name is hardcoded.
          $media_entities = $entity->field_image->referencedEntities();
          if (!empty($media_entities)) {
            foreach ($media_entities as $media_entity) {
              $selected_ids[] = (int) $media_entity->id();
            }
          }
        }
        catch (\Exception $e) {
          // log this.
        }

        $my_results = [];
        // Now need to remove from view.
        if (!empty($selected_ids)) {
          $i = 0;
          foreach ($view->result as $key =>  $item) {
            $id = (int) $item->_entity->id();
            if (!in_array($id, $selected_ids)) {
              $my_results[] = new ResultRow([
                '_entity' => $item->_entity,
                'index' => $i,
                'mid' => $item->_entity->id(),
              ]);
              $i++;
            }
          }
          $view->result = $my_results;
        }
      }
    }
  }
}

Это работает. Тем не менее, есть довольно много предположений ... Хорошо, что браузер сущностей позволяет вам выбирать, какое представление.

taggartJ
источник