- 🇮🇷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 3that 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; } }