access_policy-1.0.x-dev/src/TermHierarchy.php

src/TermHierarchy.php
<?php

namespace Drupal\access_policy;

use Drupal\Core\Database\Connection;

/**
 * Defines a class for fetching taxonomy terms in a hierarchical way.
 *
 * This is primarily to address an issue in core where TermStorage::loadTree()
 * will always filter the results by taxonomy_term_access. When used with
 * access policy that method can cause an infinite loop. Once a fix for that
 * issue lands this service can be removed.
 *
 * @see https://www.drupal.org/project/drupal/issues/2938848
 */
class TermHierarchy {

  /**
   * Array of term parents keyed by vocabulary ID and child term ID.
   *
   * @var array
   */
  protected $treeParents = [];

  /**
   * Array of term ancestors keyed by vocabulary ID and parent term ID.
   *
   * @var array
   */
  protected $treeChildren = [];

  /**
   * Array of terms in a tree keyed by vocabulary ID and term ID.
   *
   * @var array
   */
  protected $treeTerms = [];

  /**
   * Array of loaded trees keyed by a cache id matching tree arguments.
   *
   * @var array
   */
  protected $trees = [];

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The taxonomy term data table.
   *
   * @var string
   */
  protected $dataTable = 'taxonomy_term_field_data';

  /**
   * Constructs a TermHierarchy object.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   */
  public function __construct(Connection $connection) {
    $this->database = $connection;
  }

  /**
   * Finds all terms in a given vocabulary ID.
   *
   * This is copied from TermStorage::loadTree() and modified slightly to remove
   * 'taxonomy_term_access' tag.
   *
   * @param string $vid
   *   Vocabulary ID to retrieve terms for.
   * @param int $parent
   *   The term ID under which to generate the tree. If 0, generate the tree
   *   for the entire vocabulary.
   * @param int $max_depth
   *   The number of levels of the tree to return. Leave NULL to return all
   *   levels.
   *
   * @return object[]|\Drupal\taxonomy\TermInterface[]
   *   A numerically indexed array of term objects that are the children of the
   *   vocabulary $vid.
   */
  public function loadTree($vid, $parent = 0, $max_depth = NULL) {
    $cache_key = implode(':', func_get_args());
    if (!isset($this->trees[$cache_key])) {
      // We cache trees, so it's not CPU-intensive to call on a term and its
      // children, too.
      if (!isset($this->treeChildren[$vid])) {
        $this->treeChildren[$vid] = [];
        $this->treeParents[$vid] = [];
        $this->treeTerms[$vid] = [];

        $query = $this->getQuery($vid);
        $result = $query->execute();

        foreach ($result as $term) {
          $this->treeChildren[$vid][$term->parent][] = $term->tid;
          $this->treeParents[$vid][$term->tid][] = $term->parent;
          $this->treeTerms[$vid][$term->tid] = $term;
        }
      }

      $max_depth = (!isset($max_depth)) ? count($this->treeChildren[$vid]) : $max_depth;
      $tree = [];

      // Keeps track of the parents we have to process, the last entry is used
      // for the next processing step.
      $process_parents = [];
      $process_parents[] = $parent;

      // Loops over the parent terms and adds its children to the tree array.
      // Uses a loop instead of a recursion, because it's more efficient.
      while (count($process_parents)) {
        $parent = array_pop($process_parents);
        // The number of parents determines the current depth.
        $depth = count($process_parents);
        if ($max_depth > $depth && !empty($this->treeChildren[$vid][$parent])) {
          $has_children = FALSE;
          $child = current($this->treeChildren[$vid][$parent]);
          do {
            if (empty($child)) {
              break;
            }
            $term = $this->treeTerms[$vid][$child];
            if (isset($this->treeParents[$vid][$term->tid])) {
              // Clone the term so that the depth attribute remains correct
              // in the event of multiple parents.
              $term = clone $term;
            }
            $term->depth = $depth;
            unset($term->parent);
            $tid = $term->tid;
            $term->parents = $this->treeParents[$vid][$tid];
            $tree[] = $term;
            if (!empty($this->treeChildren[$vid][$tid])) {
              $has_children = TRUE;

              // We have to continue with this parent later.
              $process_parents[] = $parent;
              // Use the current term as parent for the next iteration.
              $process_parents[] = $tid;

              // Reset pointers for child lists because we step in there more
              // often with multi parents.
              reset($this->treeChildren[$vid][$tid]);
              // Move pointer so that we get the correct term the next time.
              next($this->treeChildren[$vid][$parent]);
              break;
            }
          } while ($child = next($this->treeChildren[$vid][$parent]));

          if (!$has_children) {
            // We processed all terms in this hierarchy-level, reset pointer
            // so that this function works the next time it gets called.
            reset($this->treeChildren[$vid][$parent]);
          }
        }
      }
      $this->trees[$cache_key] = $tree;
    }
    return $this->trees[$cache_key];
  }

  /**
   * Get the hierarchical query.
   *
   * The primary change that this method makes is to remove the
   * 'taxonomy_term_access' tag from the query.
   *
   * @param string $vid
   *   The vocabulary id.
   *
   * @return \Drupal\Core\Database\Query\SelectInterface
   *   The database select query.
   */
  protected function getQuery($vid) {
    $query = $this->database->select($this->dataTable, 't');
    $query->join('taxonomy_term__parent', 'p', '[t].[tid] = [p].[entity_id]');
    $query->addExpression('[parent_target_id]', 'parent');
    $query
      ->fields('t')
      ->condition('t.vid', $vid)
      ->condition('t.default_langcode', 1)
      ->orderBy('t.weight')
      ->orderBy('t.name');
    return $query;
  }

  /**
   * Reset the term hierarchy cache.
   */
  public function resetCache() {
    $this->treeChildren = [];
    $this->treeParents = [];
    $this->treeTerms = [];
    $this->trees = [];
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc