Select entity reference drop-down element

Usually when working with a list of entites in the form and you need to reference them you will probobaly use the entity_autocomplete form element. This is fine if we only need the entity ID (target_id) to work with and later load the referenced entity to get other values from it.

There are also use cases when you only need a specific field value from a list of entities in the form for users to select and spare the need for loading the referenced entity. For example, locale configuration entity which holds multiple information (fields) about the locale such as: language_codecountry_codecountry_namecharacter_setdate_format, etc.

Snippet shared in the following post extends the core select form element and adds support for selecting entity fields by introducing the new form element called select_entity_reference

Continuing with the previous locale example, to get the select list of language codes we can use it as the following example shows:

$form['language'] = [
  '#title' => $this->t('Language'),
  '#type' => 'select_entity_reference'
  '#target_type' => 'locale', // Referenced entity type ID.
  '#option_settings' => [
    'label' => 'country_name',
    'value' => 'country_code',
  ],
];

The element will load a list of locale configuration entities and use the entity field country_code for the option value and country_name as display option label.

Example of select entity element

To achieve the same list with the core select element, we would need to use the entity_type.manager to load a list of locale entities and then loop through the list of all entities to create a new array which will have value and label for the #options list.

Hope you find this simple element useful. Of course, don't go crazy with large lists, its intention is to be used on the smaller set of entities.

<?php

namespace Drupal\mymodule\Element;

use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Select;

/**
 * Select drop-down for selecting reference entity.
 *
 * @FormElement("select_entity_reference")
 */
class SelectEntityReference extends Select {

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    return parent::getInfo() + [
      '#target_type' => NULL,
      '#option_settings' => [
        'value' => 'id',
        'label' => 'label',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
    $element = parent::processSelect($element, $form_state, $complete_form);
    $entities = static::getEntityTypeManager()
      ->getStorage($element['#target_type'])
      ->loadMultiple();
    $optionLabel = $element['#option_settings']['label'];
    $optionValue = $element['#option_settings']['value'];

    foreach ($entities as $entity) {
      if ($entity instanceof ContentEntityInterface) {
        /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
        if (!$entity->hasField($optionLabel) || !$entity->hasField($optionValue)) {
          throw new \Exception('Target entity has non-existing field defined for option value and/or label in (#option_settings).');
        }
        $element['#options'][$entity->get($optionValue)->getString()] = $entity->get($optionLabel)->getString();
      }
      if ($entity instanceof ConfigEntityInterface) {
        $element['#options'][$entity->get($optionValue)] = $entity->get($optionLabel);
      }
    }
    return $element;
  }

  /**
   * Wraps entity type manager.
   *
   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
   *   Entity type manager.
   */
  protected static function getEntityTypeManager() {
    return \Drupal::service('entity_type.manager');
  }

}

 

 

Add new comment

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.