Prevent user from selecting parent items

Created on 7 July 2022, over 2 years ago
Updated 19 September 2023, about 1 year ago

Hello,

I have a taxonomy vocabulary that looks like this:

- region 1
-- country 1
--- city 1
--- city 2
-- country 2
--- city 3
--- city 4

By default, the user can select any term, including region and country. I want the user to be able to only select a city. Is there some documentation I'm missing or should that be modified in a hook?

Thank you.

💬 Support request
Status

Active

Version

1.13

Component

User interface

Created by

🇫🇷France tabestan Sargentville, Maine

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇮🇷Iran mimalef70

    For the case where we have one depth and the user can also choose parents without children. Like the following example:

    -- region 1
    - countery 1
    - country 2
    -- region 2
    - country 3
    - country 4
    -- region 3

    that the user can select all countries. Also, because region 3 does not have any children, he can choose it. For Drupal 10 I wrote this code:

    <?php
    
    namespace Drupal\select2_custom_widget\Plugin\Field\FieldWidget;
    
    use Drupal\Core\Entity\EntityTypeManagerInterface;
    use Drupal\Core\Field\FieldDefinitionInterface;
    use Drupal\Core\Field\FieldItemListInterface;
    use Drupal\Core\Form\FormStateInterface;
    use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
    use Drupal\select2\Plugin\Field\FieldWidget\Select2EntityReferenceWidget;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    
    /**
     * Provides a 'Select2 OptGroup two levels' widget.
     *
     * @FieldWidget(
     *   id = "select2_optgroup_two_levels",
     *   label = @Translation("Select2 OptGroup two levels"),
     *   field_types = {
     *     "entity_reference",
     *   },
     *   multiple_values = TRUE
     * )
     */
    class Select2OptGroupTwoLevels extends Select2EntityReferenceWidget implements ContainerFactoryPluginInterface {
    
      /**
       * The entity type manager service.
       *
       * @var \Drupal\Core\Entity\EntityTypeManagerInterface
       */
      protected $entityTypeManager;
    
      /**
       * {@inheritdoc}
       */
      public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager) {
        parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
        $this->entityTypeManager = $entity_type_manager;
      }
    
      /**
       * {@inheritdoc}
       */
      public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): Select2EntityReferenceWidget {
        return new static(
          $plugin_id,
          $plugin_definition,
          $configuration['field_definition'],
          $configuration['settings'],
          $configuration['third_party_settings'],
          $container->get('entity_type.manager')
        );
      }
    
      /**
       * {@inheritdoc}
       */
      public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
        $element = parent::formElement($items, $delta, $element, $form, $form_state);
        $element['#options'] = $this->getOptGroupOptions($items);
        return $element;
      }
    
      /**
       * Constructs the optgroup options for the select element.
       *
       * @param \Drupal\Core\Field\FieldItemListInterface $items
       *   The field items.
       *
       * @return array
       *   The optgroup options.
       */
      private function getOptGroupOptions(FieldItemListInterface $items): array {
        $field_definition = $items->getFieldDefinition();
        $handler_settings = $field_definition->getSetting('handler_settings');
        $target_bundles = $handler_settings['target_bundles'] ?? [];
        $parents_with_children = $this->getParentsWithChildren($target_bundles);
    
        $result = [];
        $current_parent_name = '';
        foreach ($target_bundles as $bundle_name => $bundle_info) {
          $terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadTree($bundle_name);
          foreach ($terms as $term) {
            if ($term->depth === 0) {
              $current_parent_name = $term->name;
              if (!isset($parents_with_children[$term->tid])) {
                $result[$term->tid] = $term->name;
              } else {
                $result[$current_parent_name] = $result[$current_parent_name] ?? [];
              }
            } elseif ($term->depth === 1) {
              $result[$current_parent_name][$term->tid] = $term->name;
            }
          }
        }
        return $result;
      }
    
      /**
       * Identifies parents that have children.
       *
       * @param array $target_bundles
       *   The target bundles.
       *
       * @return array
       *   An array of parents with children.
       */
      private function getParentsWithChildren(array $target_bundles): array {
        $parents_with_children = [];
        foreach ($target_bundles as $bundle_name => $bundle_info) {
          $terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadTree($bundle_name);
          foreach ($terms as $term) {
            if ($term->depth === 1) {
              $parents_with_children[$term->parents[0]] = true;
            }
          }
        }
        return $parents_with_children;
      }
    }
    
    
Production build 0.71.5 2024