Como passar a seleção atual para a exibição de seleção do navegador da entidade

8

Estou usando um navegador de entidade (2.x-dev no Drupal 8) como widget de formulário para o campo base de referência de entidade de uma entidade personalizada. O navegador da entidade está configurado

  • como uma exibição modal,
  • com um único widget,
  • e nenhuma exibição de seleção,
  • usando uma visualização com o campo de seleção em massa do navegador da entidade como widget e
  • anexar entidades escolhidas à seleção atual do campo de referência.

A seleção das entidades está funcionando bem. Mas o campo de referência da entidade não deve ter duplicatas.

Para facilitar a seleção de entidades sem duplicatas, gostaria de filtrar entidades já escolhidas nos resultados da exibição do navegador de entidades. Portanto, os usuários verão apenas entidades não selecionadas.

Para esse fim, criei um plug-in argumento_default de exibições personalizadas que expõe o armazenamento de seleção do navegador da entidade como argumento padrão de contexto para o ID da entidade:

<?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;
  }

}

O problema que enfrento é que a seleção atual no armazenamento de seleção está sempre vazia, independentemente de quantas entidades foram selecionadas no campo de referência da entidade, e mesmo depois de concluir a seleção modal e abrir o navegador da entidade novamente.

O que preciso fazer para que a seleção atual seja exposta no armazenamento de seleção do navegador da entidade?

Mario Steinitz
fonte
aguarde, quer dizer, se você já selecionou (e salvou) um item que não deseja que ele seja exibido no modal como um item selecionável? ou você quer dizer não permitir a seleção duplicada em tempo real ... ou quer ter dados exclusivos em todo o seu conteúdo, se selecionado, e ocultar a exibição?
taggartJ
Ambos os casos. Se a entidade for nova e o modal for usado para selecionar entidades relacionadas, a seleção feita já deverá ser filtrada no navegador da entidade, uma vez clicando no botão "selecionar" novamente (antes que a entidade tenha sido salva). E, é claro, depois que ele foi salvo e está prestes a ser reeditado, a seleção atual ( #default_value) precisa ser considerada também como filtro.
Mario Steinitz

Respostas:

4

O Navegador de entidade não passa atualmente o campo de itens de valor padrão atual em dados persistentes, mas é fácil adicioná-lo.

1) Adicione dados persistentes usando 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) Atualize sua seleção para que, se estiver em branco, mostre tudo:

  /**
   * {@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) Verifique se "excluir" e "permitir vários" estão marcados na sua seleção.

insira a descrição da imagem aqui

A propósito, se você atualizar para a versão mais recente do desenvolvedor do entity_browser, não precisará do seu plug-in personalizado. Há um novo plug- in de visualização de valor padrão entity_browser_widget_context que é configurável.

Também adicionei um problema à fila entity_browser para adicionar essas informações no widget_context.

oknate
fonte
2

Eu usei sua classe de argumento padrão e depurei um pouco. Esta é a minha abordagem:

O widget do navegador da entidade armazena os valores selecionados em sua currentpropriedade, que é preenchida quando o formulário da entidade é aberto com uma entidade / seleção existente. O widget também usa AJAX quando o modal é fechado e a currentpropriedade é atualizada de acordo.

Assim, você pode obter os IDs de entidade selecionados usando algo como o seguinte no seu formulário de entidade / alteração de formulário:

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'];
    }
  }
}

Outra propriedade de widget disponível no formulário é o contexto de widget do navegador de entidade usado. Você pode simplesmente adicionar a seleção atual ao contexto do widget e usar essas informações com o argumento padrão das visualizações (o contexto do widget é atualizado no armazenamento de seleção em cada recarregamento AJAX do widget / formulário):

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

Em seguida, altere seu 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;
  }

Com essas alterações, consegui filtrar itens selecionados da minha visualização com um filtro contextual para os IDs da entidade, escolhendo

  • Quando o filtro não estiver disponível: forneça um valor padrão, digite "Seleção do navegador da entidade"
  • Mais: Excluir

Espero que ajude!

Andreas W. Wylach
fonte
0

Não consegui que o filtro padrão funcionasse, mas tive algum sucesso ao fazer o seguinte medo:

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;
        }
      }
    }
  }
}

Isso funciona. No entanto, existem algumas suposições feitas ... O bom navegador de entidades permite que você selecione qual visualização.

taggartJ
fonte