1

Is there a way to get a list of entity reference fields by field type (similar to what is produced by $all_reference_fields = $this->entityFieldManager->getFieldMapByFieldType('entity_reference');) but filtered down to just list the entity reference fields that have target entity types and bundles that match a specified list.

I'm not seeing anything in the entityFieldManager that would make it easy to filter the results of the getFieldMap() or getFieldMapByFieldType() down to just specific reference fields I'm looking for, because the mapping doesn't contain any of the field's storage information, that contains the list of target bundles and handlers.

Looks like the entity_reference module in core doesn't provide any services that would be the logical place for something like this, so my current thinking is that I'll need to do this in two steps:

  1. Call getFieldMapByFieldType('entity_reference');.
  2. Work through every single entity type in the field map, and call buildFieldStorageDefinitions($entity_type); or something of that nature, to figure out the target entity_type:bundle_name combinations, and track down fields that reference 'taxonomy_term:tag'. This seems horribly inefficient, due to the sheer number of entity_reference type fields in this site, so maybe there is a better way to do this step?

In an ideal world, there'd be just a set of database calls I could make, or even a service in core for entity_reference module! (wishful thinking) ;)

2
  • Doh! entity_reference module is deprecated already. Trying to track down where else in the codebase this stuff might be located. Commented Feb 16, 2019 at 2:47
  • I found some sample code that might work in field.module's field_entity_bundle_delete() ... Commented Feb 16, 2019 at 3:00

4 Answers 4

2

The one-step way to do what you're looking for is FieldStorageConfig::loadByProperties.

For example, to find all entity reference fields with target type of taxonomy_term, the following code will work:

$taxonomy_fields = \Drupal::entityTypeManager->getStorage('field_storage_config')
  ->loadByProperties([
     'type' => 'entity_reference',
     'settings' => [ 'target_type' => 'taxonomy_term' ],
    ]);

(This will also work without explicitly including the 'type' => 'entity_reference' since other types of fields won't have a target_type in their settings, but including it makes the code easier to understand.)

And of course, this could be made more generic so that you're passing in the target_type to match for. Of course, if you want to know what specific vocabularies are in use for a given taxonomy field, that's in the individual FieldConfig's on each bundle — so for that you'd have to load and examine each configuration for a given field on a bundle type.

For anyone doing follow-up research, the key for me figuring this out was to look at exports of field storage configurations so I could figure out what I was trying to match to in the loadByProperties call.

0
2

Answering this myself based on the investigation I did using field_entity_bundle_delete for inspiration. Would be great if someone could take this code and improve it.

/**
 * Helper method gets a list of all entity reference fields that reference
 * the specified entity type.
 *
 * @param $entity_type string
 * @param $entity_bundle string (optional)
 * @return array
 *   Filtered field map of entity reference fields.
 */
protected function getEntityReferenceFieldsByEntityType($entity_type, $entity_bundle = '') {
  // Gather a list of all entity reference fields.
  $map = \Drupal::service('entity_field.manager')->getFieldMapByFieldType('entity_reference');
  $ids = [];
  foreach ($map as $type => $info) {
    foreach ($info as $name => $data) {
      foreach ($data['bundles'] as $bundle_name) {
        $ids[] = "$type.$bundle_name.$name";
      }
    }
  }
  // Determine if any of the reference fields reference a specific entity type
  // and bundle type.
  $filtered_map = [];
  foreach (FieldConfig::loadMultiple($ids) as $field_config) {
    $field_name = $field_config->getName();
    $target_type = $field_config->getSetting('target_type');
    if (!empty($target_type) && $target_type == $entity_type) {
      if (!empty($entity_bundle)) {
        $handler_settings = $field_config->getSetting('handler_settings');
        if (isset($handler_settings['target_bundles'][$entity_bundle])) {
          $filtered_map[$entity_type][$field_name] = $map[$entity_type][$field_name];
        }
      } else {
        $filtered_map[$entity_type][$field_name] = $map[$entity_type][$field_name];
      }
    }
  }
  return $filtered_map;
}
2

This is a generic function I'm normally using from https://cornel.co/article/get-list-fields-type

function getFieldsByTypeAsOptions(string $type, ?array $settings = NULL): array {
  $properties = ['type' => $type];
  if ($settings) {
    $properties = ['settings' => $settings];
  }
  $entityTypeManager = \Drupal::entityTypeManager();
  $entityRefFields = $entityTypeManager
    ->getStorage('field_storage_config')
    ->loadByProperties($properties);
  $options = ['' => '- Select -'];
  foreach ($entityRefFields as $entityRefField) {
    $fieldName = $entityRefField->getName();
    $options[$fieldName] = $fieldName;
  }
  return $options;
}
0

I use this for deleting orphaned references

/**
 * Remove Orphaned References
 *
 * @command database_hacks:deleteOrphanedReferences
 * @aliases deleteOrphanedReferences
 */
public function deleteOrphanedReferences(): void
{
    $taxonomy_fields = Drupal::entityTypeManager()->getStorage('field_storage_config')->loadByProperties([
        'type' => 'entity_reference',
        'settings' => [ 'target_type' => 'taxonomy_term' ],
    ]);

    $connection = Drupal::database();
    foreach ($taxonomy_fields as $field) {
        $tableName = str_replace('.', '__', $field->get('originalId'));
        $fieldName = $field->get('field_name') . '_target_id';

        $sql = <<<TXT
            DELETE i
            FROM {$tableName} i
            LEFT JOIN taxonomy_term_field_data d
            ON i.{$fieldName} = d.tid
            WHERE d.name IS NULL;
TXT;

        $connection->query($sql);
        GeneralHelper::message($sql, 'status');
    }

    $sql = <<<TXT
        DELETE i
        FROM taxonomy_index i
        LEFT JOIN taxonomy_term_field_data d
        ON i.tid = d.tid
        WHERE d.name IS NULL;
TXT;

    $connection->query($sql);
    GeneralHelper::message($sql, 'status');
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.