You are here

class StrawSelection in Super Term Reference Autocomplete Widget 8

Provides specific access control for the taxonomy_term entity type.

Plugin annotation


@EntityReferenceSelection(
  id = "straw",
  label = @Translation("Straw selection"),
  entity_types = {"taxonomy_term"},
  group = "straw",
  weight = 1
)

Hierarchy

Expanded class hierarchy of StrawSelection

File

src/Plugin/EntityReferenceSelection/StrawSelection.php, line 22

Namespace

Drupal\straw\Plugin\EntityReferenceSelection
View source
class StrawSelection extends TermSelection {

  /**
   * {@inheritdoc}
   */
  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
    $options = [];
    $bundles = $this->entityTypeBundleInfo
      ->getBundleInfo('taxonomy_term');
    $handler_settings = $this->configuration['handler_settings'];
    $bundle_names = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : array_keys($bundles);
    foreach ($bundle_names as $bundle) {
      if ($vocabulary = Vocabulary::load($bundle)) {
        foreach ($this
          ->getSelectableTerms($vocabulary
          ->id()) as $term) {
          $referenceable = TRUE;
          if ($match && $match_operator == "CONTAINS") {
            $referenceable = stripos($term['tree_path'], $match) !== FALSE;
          }
          if ($match && $match_operator == "STARTS_WITH") {
            $referenceable = stripos($term['tree_path'], $match) === 0;
          }
          if ($match && $match_operator == "=") {
            $referenceable = $term['tree_path'] == $match;
          }
          if ($referenceable) {
            $options[$vocabulary
              ->id()][$term['tid']] = Html::escape($term['tree_path']);
            if ($limit > 0 && count($options[$vocabulary
              ->id()]) == $limit) {
              break;
            }
          }
        }
      }
    }
    return $options;
  }

  /**
   * Gets the potentially selectable terms for a given bundle.
   *
   * @param string $bundle_name
   *   Vocabulary ID to retrieve terms for.
   *
   * @return array
   *   The searchable data.
   */
  private function getSelectableTerms($bundle_name) {
    $cache_context_keys = \Drupal::service('cache_contexts_manager')
      ->convertTokensToKeys([
      'user.permissions',
    ])
      ->getKeys();
    $cid = $bundle_name . ':' . implode(':', $cache_context_keys);
    $straw_cache = \Drupal::cache('straw');

    // Load from cache if possible rather than rebuilding the term list.
    if ($cached_data = $straw_cache
      ->get($cid)) {
      return $cached_data->data;
    }
    $all_terms = $this->entityTypeManager
      ->getStorage('taxonomy_term')
      ->loadTree($bundle_name);

    // We want $terms to be keyed by ID rather than numerically.
    $all_terms = array_reduce($all_terms, function ($carry, $item) {
      $carry[$item->tid] = $item;
      return $carry;
    }, []);
    $searchable_data = [];
    foreach ($all_terms as $term) {

      // Build the tree path for the term, including the names of its
      // ancestors. Currently, a single term being in multiple places in the
      // hierarchy is not actively supported (only one possible tree path can
      // get shown in the autocomplete results)
      $tree_path = $term->name;
      $current = $term;
      while (($parent_id = $current->parents[0]) && ($parent = $all_terms[$parent_id])) {
        $tree_path = $parent->name . ' >> ' . $tree_path;
        $current = $parent;
      }
      $searchable_data[] = [
        'tid' => $term->tid,
        'tree_path' => $tree_path,
      ];
    }

    // Save into cache for faster loading in the future.
    \Drupal::cache('straw')
      ->set($cid, $searchable_data, Cache::PERMANENT, [
      'taxonomy_term_list',
    ]);
    return $searchable_data;
  }

  /**
   * {@inheritdoc}
   */
  public function createNewEntity($entity_type_id, $bundle, $label, $uid) {

    // Straw only works with terms; $entity_type_id should always be
    // "taxonomy_term". Since we need term-specific handling here, we ignore
    // that setting (as well as $uid, since terms don't implement
    // EntityOwnerInterface).
    $term_names = explode('>>', $label);

    /** @var \Drupal\straw\NewTermStorage $new_term_storage */
    $new_term_storage = \Drupal::service('straw.new_term_storage');

    // Tracks the deepest term we've already processed.
    $last_term = NULL;

    // Loop to find the term deepest in the existing hierarchy which matches
    // the desired tree path.
    $tree_path = '';
    while ($term_name = array_shift($term_names)) {
      $tree_path .= ($tree_path ? ' >> ' : '') . trim($term_name);
      $matching_terms_by_vocabulary = $this
        ->getReferenceableEntities($tree_path, '=', 1);
      if (empty($matching_terms_by_vocabulary[$bundle])) {

        // We'll process the unmatched term again, below, to create it.
        array_unshift($term_names, $term_name);
        break;
      }
      $last_term = key($matching_terms_by_vocabulary[$bundle]);
    }

    // Create terms of the tree path which have not been found (meaning that
    // they don't exist). There *should* always be at least one of these if
    // this function is getting called, though it should still function if there
    // isn't. The NewTermStorage service is used to track which terms have
    // already been created but which have not yet necessarily been saved, to
    // prevent creating duplicate terms if the same new term is named by
    // multiple term hierarchies being created during the same request.
    while ($term_name = array_shift($term_names)) {
      $tree_path .= ($tree_path ? ' >> ' : '') . trim($term_name);
      if ($found_term = $new_term_storage
        ->get($bundle, $tree_path)) {
        $last_term = $found_term;
      }
      else {
        $last_term = Term::create([
          'vid' => $bundle,
          'name' => trim($term_name),
          'parent' => $last_term,
        ]);
        $new_term_storage
          ->set($bundle, $tree_path, $last_term);
      }
    }
    return $last_term;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DefaultSelection::$currentUser protected property The current user.
DefaultSelection::$deprecatedProperties protected property
DefaultSelection::$entityFieldManager protected property The entity field manager service.
DefaultSelection::$entityRepository protected property The entity repository.
DefaultSelection::$entityTypeBundleInfo public property Entity type bundle info service.
DefaultSelection::$entityTypeManager protected property The entity type manager service.
DefaultSelection::$moduleHandler protected property The module handler service.
DefaultSelection::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 2
DefaultSelection::elementValidateFilter public static function Form element validation handler; Filters the #value property of an element.
DefaultSelection::reAlterQuery protected function Helper method: Passes a query to the alteration system again.
DefaultSelection::validateConfigurationForm public function Form validation handler. Overrides SelectionPluginBase::validateConfigurationForm
DefaultSelection::validateReferenceableEntities public function Validates which existing entities can be referenced. Overrides SelectionInterface::validateReferenceableEntities
DefaultSelection::__construct public function Constructs a new DefaultSelection object. Overrides SelectionPluginBase::__construct 1
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DeprecatedServicePropertyTrait::__get public function Allows to access deprecated/removed properties.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
SelectionPluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies
SelectionPluginBase::ensureBackwardCompatibilityConfiguration protected function Ensures a backward compatibility level configuration.
SelectionPluginBase::entityQueryAlter public function Allows the selection to alter the SelectQuery generated by EntityFieldQuery. Overrides SelectionInterface::entityQueryAlter 2
SelectionPluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
SelectionPluginBase::resolveBackwardCompatibilityConfiguration protected function Moves the backward compatibility level configurations in the right place.
SelectionPluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration
SelectionPluginBase::submitConfigurationForm public function Form submission handler. Overrides PluginFormInterface::submitConfigurationForm
StrawSelection::createNewEntity public function Creates a new entity object that can be used as a valid reference. Overrides TermSelection::createNewEntity
StrawSelection::getReferenceableEntities public function Gets the list of referenceable entities. Overrides TermSelection::getReferenceableEntities
StrawSelection::getSelectableTerms private function Gets the potentially selectable terms for a given bundle.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TermSelection::buildConfigurationForm public function Form constructor. Overrides DefaultSelection::buildConfigurationForm
TermSelection::buildEntityQuery protected function Builds an EntityQuery to get referenceable entities. Overrides DefaultSelection::buildEntityQuery
TermSelection::countReferenceableEntities public function Counts entities that are referenceable. Overrides DefaultSelection::countReferenceableEntities
TermSelection::defaultConfiguration public function Gets default configuration for this plugin. Overrides DefaultSelection::defaultConfiguration
TermSelection::validateReferenceableNewEntities public function Validates which newly created entities can be referenced. Overrides DefaultSelection::validateReferenceableNewEntities