Account created on 8 April 2016, about 8 years ago
#

Merge Requests

Recent comments

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Finally! Congratulations!

πŸ‡ΊπŸ‡¦Ukraine nikita_tt
<?php

namespace Drupal\customer_portal\Plugin\facets\processor;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\facets\FacetInterface;
use Drupal\facets\Plugin\facets\processor\HierarchyProcessor;
use Drupal\facets\Result\Result;
use Drupal\taxonomy\Entity\Term;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * The hierarchy processor transforms the result in a tree.
 *
 * This processor must be run before the URL processor.
 *
 * @FacetsProcessor(
 *   id = "child_hierarchy_processor",
 *   label = @Translation("Build hierarchy tree only with children terms"),
 *   description = @Translation("Build the tree for facets that use hierarchy."),
 *    stages = {
 *     "build" = 100,
 *   },
 *   locked = false,
 *   default_enabled = false,
 * )
 */
class ChildHierarchyProcessor extends HierarchyProcessor implements ContainerFactoryPluginInterface {

  /**
   * The current route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs a new object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The current route match.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->routeMatch = $route_match;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('current_route_match')
    );
  }

  /**
   * An array of all entity ids in the active resultset which are a child.
   *
   * @var string[]
   */
  protected $childIds = [];

  /**
   * {@inheritdoc}
   */
  public function build(FacetInterface $facet, array $results) {
    // Handle hierarchy.
    if ($results) {
      $keyed_results = [];
      foreach ($results as $result) {
        $keyed_results[$result->getRawValue()] = $result;
      }
      $hierarchy = $facet->getHierarchyInstance();
      $facet->addCacheableDependency($hierarchy);

      $parent_groups = $hierarchy->getChildIds(array_keys($keyed_results));
      $keyed_results = $this->buildAdvancedHierarchicalTree($keyed_results, $parent_groups, $facet);

      // Remove children from primary level.
      foreach (array_unique($this->childIds) as $child_id) {
        unset($keyed_results[$child_id]);
      }

      // Display only children of the current taxonomy term.
      if ($current_term_id = $this->getCurrentTermId($facet, $results, $keyed_results)) {
        if (array_key_exists($current_term_id, $keyed_results)) {
          $keyed_results = $keyed_results[$current_term_id]->getChildren();
          // Set facet name.
          $this->prepareFacetName($facet, $current_term_id);
        }
        // If the term is not in the list of top level terms then this most
        // likely means this term in the deepest level of tree that can be
        // displayed using this facet widget.
        // So at first, it is needed to check if any of the parents of the
        // current term is in the list of options. And if so then remove all
        // options ("all" because the current term is at the bottom of hierarchy
        // and it cannot have any options).
        else {
          $parent_term_ids = $hierarchy->getParentIds($current_term_id);
          if (array_intersect($parent_term_ids, array_keys($keyed_results))) {
            $deepest_possible_level = TRUE;
          }
        }
      }
      $results = !empty($deepest_possible_level) ? [] : array_values($keyed_results);
    }
    return $results;
  }

  /**
   * Prepares facet name.
   *
   * @param \Drupal\facets\FacetInterface $facet
   *   Facet instance.
   * @param null|string $term_id
   *   Term ID.
   * @param array $other_data
   *   Something else.
   */
  protected function prepareFacetName($facet, $term_id = NULL, array $other_data = []) {
    if ($facet->get('show_title') === TRUE && ($term = Term::load($term_id))) {
      $facet->set('name', $term->label());
    }
  }

  /**
   * Find the term ID that is the parent of options that must be shown.
   *
   * @param \Drupal\facets\FacetInterface $facet
   *   Facet instance.
   * @param \Drupal\facets\Result\Result[] $results
   *   Facet results.
   * @param \Drupal\facets\Result\Result[] $keyed_results
   *   Processed results.
   *
   * @return string|null
   *   Taxonomy term ID.
   */
  protected function getCurrentTermId(FacetInterface $facet, array $results, array $keyed_results) {
    $current_term_id = NULL;
    if ($this->routeMatch->getRouteName() === 'entity.taxonomy_term.canonical') {
      $current_term_id = $this->routeMatch->getRawParameter('taxonomy_term');
    }
    return $current_term_id;
  }

  /**
   * Builds a hierarchical structure for results.
   *
   * When given an array of results and an array which defines the hierarchical
   * structure, this will build the results structure and set all childs.
   *
   * @param \Drupal\facets\Result\ResultInterface[] $keyed_results
   *   An array of results keyed by id.
   * @param array $parent_groups
   *   An array of 'child id arrays' keyed by their parent id.
   *
   * @return \Drupal\facets\Result\ResultInterface[]
   *   An array of results structured hierarchically.
   */
  protected function buildAdvancedHierarchicalTree(array $keyed_results, array $parent_groups, $facet): array {
    $processors = $facet->getProcessorsByStage('build');
    if (isset($processors['translate_entity'])) {
      /** @var \Drupal\facets\Plugin\facets\processor\TranslateEntityProcessor $translate_entity_processor */
      $translate_entity_processor = $processors['translate_entity'];
    }

    foreach ($keyed_results as &$result) {
      $current_id = $result->getRawValue();
      if (isset($parent_groups[$current_id]) && $parent_groups[$current_id]) {
        $child_ids = $parent_groups[$current_id];
        $child_keyed_results = [];
        foreach ($child_ids as $child_id) {
          if (isset($keyed_results[$child_id])) {
            $child_keyed_results[$child_id] = $keyed_results[$child_id];
          }
          else {
            $child_keyed_results[$child_id] = new Result($facet, $child_id, $child_id, 0);
          }
        }

        // Set active status for child options that have been just created.
        $facet->setResults($child_keyed_results);

        // Display entity labels instead of entity IDs.
        if (!empty($translate_entity_processor)) {
          $child_keyed_results = $translate_entity_processor->build($facet, $child_keyed_results);
        }

        $result->setChildren($child_keyed_results);
        $this->childIds = array_merge($this->childIds, $child_ids);
      }
    }

    return $keyed_results;
  }

}
πŸ‡ΊπŸ‡¦Ukraine nikita_tt

@paulrad Thank you for the patch! Wouldn't it be better to have this code in a separate sub-module? What do you think?

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

More details are here: https://www.drupal.org/project/country/issues/3196869#comment-15377861 πŸ› Autocomplete field widget error in case of BaseField Postponed: needs info

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Hi. As I remember there was a problem if the field was a base field (defined in the entity class) of the custom entity type.

Steps to reproduce:
1. Create a custom entity type. It can be generated via drush or drupal console.
2. Add the country field to this custom entity type. (baseFieldDefinitions method)

$fields['residence_country'] = BaseFieldDefinition::create('country')
      ->setLabel(t('Country of residence'))
      ->setRevisionable(TRUE)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE)
      ->setRequired(TRUE);

3. Go to the entity create/edit form and try to add a value to the Country field. You will see the error.

Note: I'm not sure whether the issue is still relevant or not.

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Thanks again, @srilakshmier, for the patch! The new dev branch has been created and patched, and the new release (2.0.0) has been published.

I've used the commit message proposed in this ticket for adding credits for @srilakshmier but I'm not sure if it worked correctly.

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Thanks! The lowest supported Drupal version was changed to 9.5 because of PHP8 requirements.
See https://www.drupal.org/docs/getting-started/system-requirements/php-requ... β†’

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

nikita_tt β†’ made their first commit to this issue’s fork.

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Thanks, @srilakshmier, for the patch!
Can someone advise what way should I choose for applying this patch? I'm in doubt because the current latest version of the module (8.x-1.7) supports 8 AND 10 versions of Drupal but this new hook is available only for 9.2.x and higher.
Should I change the required Drupal version for the latest module version and create a new release only for D10?

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

I agree with #10. This patch does not make any sense.

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

Thank you! The new tag has been added.

πŸ‡ΊπŸ‡¦Ukraine nikita_tt

nikita_tt β†’ made their first commit to this issue’s fork.

Production build 0.69.0 2024