Comment passer la sélection actuelle à la vue de sélection du navigateur d'entités

8

J'utilise un navigateur d'entités (2.x-dev dans Drupal 8) comme widget de formulaire pour le champ de base de référence d'entité d'une entité personnalisée. Le navigateur d'entités est configuré

  • comme affichage modal,
  • avec un seul widget,
  • et pas d'affichage de sélection,
  • utiliser une vue avec le champ de sélection en masse du navigateur d'entités comme widget, et
  • pour ajouter les entités choisies à la sélection actuelle du champ de référence.

La sélection des entités fonctionne correctement. Mais le champ de référence d'entité ne doit pas avoir de doublons.

Afin de faciliter la sélection des entités sans doublons, je voudrais filtrer les entités déjà choisies dans les résultats de la vue du navigateur d'entités. Les utilisateurs ne verront donc que les entités non sélectionnées.

À cette fin, j'ai créé un plug-in de vues personnalisées argument_default qui expose le stockage de sélection du navigateur d'entités comme argument par défaut du contexte pour l'ID d'entité:

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

}

Le problème auquel je suis confronté est que la sélection actuelle dans le stockage de sélection est toujours vide, quel que soit le nombre d'entités sélectionnées dans le champ de référence d'entité, et même après avoir terminé la sélection modale et ouvert à nouveau le navigateur d'entités.

Que dois-je faire pour que la sélection actuelle soit exposée dans le stockage de sélection du navigateur d'entité?

Mario Steinitz
la source
attendez donc vous voulez dire que si vous avez tout prêt sélectionné (et enregistré) un élément que vous ne voulez pas qu'il affiche dans le modal comme un élément sélectionnable? ou voulez-vous dire ne pas autoriser la sélection en double à la volée ... ou voulez-vous dire que vous voulez des données uniques sur tout votre contenu si elles sont sélectionnées, puis masquées de la vue?
taggartJ
Les deux cas. Si l'entité est nouvelle et que le modal est utilisé pour sélectionner des entités liées, la sélection effectuée doit déjà être filtrée à partir du navigateur d'entités, en cliquant à nouveau sur le bouton "sélectionner" (avant que l'entité ait été enregistrée). Et bien sûr, après avoir été enregistré et sur le point d'être réédité, la sélection actuelle ( #default_value) doit également être considérée comme un filtre.
Mario Steinitz

Réponses:

4

Le navigateur d'entités ne transmet pas actuellement le champ actuel des éléments de valeur par défaut dans les données persistantes, mais il est facile de l'ajouter.

1) Ajoutez des données persistantes à l'aide de 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) Mettez à jour votre sélection de sorte que si elle est vide, elle affiche tout:

  /**
   * {@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) Assurez-vous d'avoir coché «exclure» et «autoriser plusieurs» dans votre sélection.

entrez la description de l'image ici

Par ailleurs, si vous mettez à jour vers la dernière version de développement de entity_browser, vous n'avez pas besoin de votre plugin personnalisé. Il existe un nouveau plugin de vues de valeur par défaut entity_browser_widget_context qui est configurable.

J'ai également ajouté un problème à la file d' attente entity_browser pour ajouter ces informations dans le widget_context.

oknate
la source
2

J'ai utilisé votre classe d'arguments par défaut et débogué un peu. Voici mon approche:

Le widget du navigateur d'entités stocke les valeurs sélectionnées dans sa currentpropriété, qui est remplie, lorsque le formulaire d'entité est ouvert avec une entité / sélection existante. Le widget utilise également AJAX à la fermeture du modal et la currentpropriété est mise à jour en conséquence.

Ainsi, vous pouvez obtenir les ID d'entité sélectionnés en utilisant quelque chose comme ce qui suit dans votre formulaire d'entité / formulaire de modification:

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

Une autre propriété de widget disponible dans le formulaire est le contexte de widget du navigateur d'entités utilisé. Vous pouvez simplement ajouter la sélection actuelle au contexte du widget et utiliser ces informations avec l'argument par défaut de votre vue (le contexte du widget est mis à jour dans le stockage de sélection à chaque rechargement AJAX du widget / formulaire):

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

Ensuite, modifiez votre 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;
  }

Avec ces modifications, j'ai pu filtrer les éléments sélectionnés de ma vue avec un filtre contextuel pour les ID d'entité, en choisissant

  • Lorsque le filtre n'est pas disponible: indiquez une valeur par défaut, tapez "Sélection du navigateur d'entités"
  • Plus: Exclure

J'espère que cela aide!

Andreas W. Wylach
la source
0

Je n'ai pas pu faire fonctionner le filtre par défaut, mais j'ai réussi à faire l'effroi suivant:

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

Cela fonctionne. Cependant, il y a quelques hypothèses faites ... Heureusement, le navigateur d'entités vous permet de sélectionner quelle vue.

taggartJ
la source