Apologies if this is a duplicate but a search did not reveal anyone else reporting this issue that I could find.

Problem/Motivation

When adding a field referencing Vocabulary configuration entities, the weight of the Vocabularies seems to be ignored on the Entity Reference field widgets.

Example

Given the following configured vocabularies created in the following order and weights set as below:

- X (weight 2)
- Y (weight 3)
- Z (weight 1)

Using the options_buttons field widget with unlimited cardinality would expect to see three checkboxes in the following order, following the weight of the vocabulary:

- Z
- X
- Y

What I actually see is an alphabetical list of the vocabularies, regardless of weight. This list seems to be sorted alphabetically on the vocabulary machine name. If you were to add a new Vocabulary called 'A' it would appear at the top of this list.

- X
- Y
- Z

This is a real issue for me at the moment because we're working on a site with over 100 vocabularies and this order does not follow the order the customer has configured in the CMS which is making it very hard to find specific items, especially once vocabularies names are tweaked to be different from their machine names.

Proposed resolution

I'm not sure, I couldn't find the specific lines of code in the entity reference fields which load in the config entities, however if these cant be sorted/returned by the storage interface then perhaps it needs to be done separately afterwards? I'm pretty sure database entities don't have this problem and a list of Terms would be sorted correctly.

Comments

orphans created an issue. See original summary.

mattjones86’s picture

UPDATE: So the default ConfigEntityListBuilder class has a load method which sorts the results like so:

  /**
   * {@inheritdoc}
   */
  public function load() {
    $entity_ids = $this->getEntityIds();
    $entities = $this->storage->loadMultipleOverrideFree($entity_ids);

    // Sort the entities using the entity class's sort() method.
    // See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
    uasort($entities, [$this->entityType->getClass(), 'sort']);
    return $entities;
  }

I would propose to also sort the results in ConfigEntityStorage in the same way?


  /**
   * {@inheritdoc}
   */
  protected function doLoadMultiple(array $ids = NULL) {
    $prefix = $this->getPrefix();

    // Get the names of the configuration entities we are going to load.
    if ($ids === NULL) {
      $names = $this->configFactory->listAll($prefix);
    }
    else {
      $names = [];
      foreach ($ids as $id) {
        // Add the prefix to the ID to serve as the configuration object name.
        $names[] = $prefix . $id;
      }
    }

    // Load all of the configuration entities.
    /** @var \Drupal\Core\Config\Config[] $configs */
    $configs = [];
    $records = [];
    foreach ($this->configFactory->loadMultiple($names) as $config) {
      $id = $config->get($this->idKey);
      $records[$id] = $this->overrideFree ? $config->getOriginal(NULL, FALSE) : $config->get();
      $configs[$id] = $config;
    }
    $entities = $this->mapFromStorageRecords($records, $configs);

    // Config entities wrap config objects, and therefore they need to inherit
    // the cacheability metadata of config objects (to ensure e.g. additional
    // cacheability metadata added by config overrides is not lost).
    foreach ($entities as $id => $entity) {
      // But rather than simply inheriting all cacheability metadata of config
      // objects, we need to make sure the self-referring cache tag that is
      // present on Config objects is not added to the Config entity. It must be
      // removed for 3 reasons:
      // 1. When renaming/duplicating a Config entity, the cache tag of the
      //    original config object would remain present, which would be wrong.
      // 2. Some Config entities choose to not use the cache tag that the under-
      //    lying Config object provides by default (For performance and
      //    cacheability reasons it may not make sense to have a unique cache
      //    tag for every Config entity. The DateFormat Config entity specifies
      //    the 'rendered' cache tag for example, because A) date formats are
      //    changed extremely rarely, so invalidating all render cache items is
      //    fine, B) it means fewer cache tags per page.).
      // 3. Fewer cache tags is better for performance.
      $self_referring_cache_tag = ['config:' . $configs[$id]->getName()];
      $config_cacheability = CacheableMetadata::createFromObject($configs[$id]);
      $config_cacheability->setCacheTags(array_diff($config_cacheability->getCacheTags(), $self_referring_cache_tag));
      $entity->addCacheableDependency($config_cacheability);
    }

    // Sort in the same way as the list builder!???
    uasort($entities, [$this->entityType->getClass(), 'sort']);
    return $entities;
  }

In fact sorting in the list builder probably isn't required if it's being done at the storage level. Thoughts?

mattjones86’s picture

StatusFileSize
new671 bytes
mattjones86’s picture

StatusFileSize
new674 bytes

Whoops some white space got in there.

andypost’s picture

Version: 8.5.3 » 8.7.x-dev
Status: Active » Needs work
Related issues: +#1821274: Add back ability to sort on vocabulary weight and name
andypost’s picture