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_code
, country_code
, country_name
, character_set
, date_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.
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