Cómo pasar la selección actual a la vista de selección del navegador de entidades

8

Estoy usando un navegador de entidades (2.x-dev en Drupal 8) como widget de formulario para el campo base de referencia de entidad de una entidad personalizada. El navegador de la entidad está configurado

  • como una pantalla modal,
  • con un solo widget,
  • y sin pantalla de selección,
  • utilizando una vista con el campo de selección masiva del navegador de entidades como widget, y
  • para agregar entidades elegidas a la selección actual del campo de referencia.

Seleccionar las entidades está funcionando bien. Pero el campo de referencia de la entidad no tendrá duplicados.

Para facilitar la selección de entidades sin duplicados, me gustaría filtrar las entidades ya elegidas de los resultados de la vista del navegador de entidades. Por lo tanto, los usuarios solo verán entidades no seleccionadas.

Para este propósito, creé un complemento predeterminado de argumentos personalizados de vistas que expone el almacenamiento de selección del navegador de entidades como argumento predeterminado de contexto para la ID de entidad:

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

}

El problema que enfrento es que la selección actual dentro del almacenamiento de selección siempre está vacía, sin importar cuántas entidades se hayan seleccionado en el campo de referencia de la entidad, e incluso después de completar la selección modal y volver a abrir el navegador de la entidad.

¿Qué debo hacer para que la selección actual quede expuesta en el almacenamiento de selección del navegador de la entidad?

Mario Steinitz
fuente
espere, ¿quiere decir que si tiene todo listo (y guardado) un elemento que no desea que el elemento se muestre en el modal como un elemento seleccionable? o quiere decir que no permite la selección duplicada sobre la marcha ... o quiere decir que desea datos únicos en todo su contenido si se selecciona y luego se oculta de la vista?
taggartJ
Ambos casos. Si la entidad es nueva y el modal se usa para seleccionar entidades relacionadas, entonces la selección realizada ya se filtrará desde el navegador de la entidad, una vez que haga clic nuevamente en el botón "seleccionar" (antes de que la entidad se haya guardado). Y, por supuesto, después de que se haya guardado y esté a punto de ser reeditado, la selección actual ( #default_value) también debe considerarse como filtro.
Mario Steinitz

Respuestas:

4

El navegador de entidades no pasa actualmente el campo de elementos de valor predeterminado actual en datos persistentes, pero es fácil agregarlo.

1) Agregue datos 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) Actualice su selección para que, si está en blanco, muestre todo:

  /**
   * {@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) Asegúrese de haber marcado "excluir" y "permitir múltiples" en su selección.

ingrese la descripción de la imagen aquí

Por cierto, si actualiza a la última versión de desarrollo de entity_browser, no necesita su complemento personalizado. Hay un nuevo complemento de vistas de valor predeterminado de entidad_explorador_widget_contexto que es configurable.

También agregué un problema a la cola entity_browser para agregar esta información cuando estaba en el widget_context.

oknate
fuente
2

Usé su clase de argumento predeterminada y la depuré un poco. Este es mi enfoque:

El widget del navegador de entidades almacena los valores seleccionados en su currentpropiedad, que se completa, cuando el formulario de entidad se abre con una entidad / selección existente. El widget también usa AJAX cuando el modal se cierra y la currentpropiedad se actualiza en consecuencia.

Por lo tanto, puede obtener los ID de entidad seleccionados usando algo como lo siguiente en su formulario de entidad / formulario alter:

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

Otra propiedad de widget disponible en el formulario es el contexto de widget del navegador de entidad utilizado. Simplemente puede agregar la selección actual al contexto del widget y usar esta información con su argumento predeterminado de vistas (el contexto del widget se actualiza en el almacenamiento de selección en cada recarga AJAX del widget / formulario):

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

Luego modifique su 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;
  }

Con estos cambios pude filtrar los elementos seleccionados desde mi vista con un filtro contextual para las ID de entidad, eligiendo

  • Cuando el filtro no está disponible: proporcione un valor predeterminado, escriba "Selección del navegador de entidades"
  • Más: excluir

¡Espero eso ayude!

Andreas W. Wylach
fuente
0

No pude lograr que el filtro predeterminado funcionara, pero tuve algo de éxito haciendo lo siguiente:

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

Esto funciona Sin embargo, hay bastantes suposiciones hechas ... Lo bueno es que el navegador de entidades le permite seleccionar qué vista.

taggartJ
fuente