Find all entity reference fields that target particular bundles

The problem

What do you do when a client comes to you in a panic and says they updated an image on a page, but afterwards they found that all of their other pages all of a sudden were displaying this same image?

Well, after thinking for a few minutes about what could have possibly caused this insanity, and after inspecting the content you might realize a couple of things:

  1. The field they were editing was an entity reference field, specifically to your Image media type.
  2. The field uses the Entity browser widget.
  3. The field has the Edit button enabled.

Darnit. The Entity browser Edit button feature strikes again. Why does that feature even exist?

Besides helping the client walk back their content edit, we need to update all the Entity browser widgets we have configured to remove the Edit button so this can't happen again. When you have dozens and dozens of entity bundles which use this widget, the thought of inspecting all of the form displays for all of these bundles gets depressing very quickly.

So, how do we quickly update all of our form displays such that all of the Entity browser widgets have the Edit button removed?

The solution

Widget configuration for a particular bundle form display is stored in an entity_form_display configuration object. Thus, we need to find all of the entity form display configuration objects where one or more components (fields) are configured to use the Entity browser widget and have the Edit button enabled. We then need to update all of those objects with corrected configuration, save them, and then export them.

How do we find all of the pertinent entity form display configuration which needs correcting? Ultimately, this is what I ended up with:

<?php

// Find all entity reference fields which reference our image media bundle.
$query = \Drupal::entityTypeManager()->getStorage('field_config')->getQuery();
$image_reference_fields = $query->condition('field_type', 'entity_reference')
  ->condition('settings.handler_settings.target_bundles.image', 'image')
  ->execute();

// Iterate over all of the image reference fields.
foreach ($image_reference_fields as $image_reference_field) {
  // Get the entity type, bundle, and field name into usable variables.
  [$entity_type, $bundle, $field_name] = explode('.', $image_reference_field);

  // Load all of the form displays for the given bundle.
  /** @var \Drupal\Core\Entity\Entity\EntityFormDisplay[] $form_displays */
  $form_displays = \Drupal::entityTypeManager()->getStorage('entity_form_display')->loadByProperties([
    'targetEntityType' => $entity_type,
    'bundle' => $bundle,
  ]);

  // For every bundle's configured form displays, check the component
  // configuration for the field. If it has its field_widget_edit settings set
  // to TRUE, then change it to FALSE and save the form display entity.
  foreach ($form_displays as $form_display) {
    $component = $form_display->getComponent($field_name);
    if (!empty($component) && !empty($component['settings']['field_widget_edit'])) {
      $component['settings']['field_widget_edit'] = FALSE;
      $form_display->setComponent($field_name, $component)
        ->save();
    }
  }
}

The comments more or less explain the logic here. You can use the above script to remove the Edit button from any entity browser widget for all form displays for all entity bundles with the id of image.

You can run the above script against your Drupal site fairly easily by using Drush's php:script command:

# Assuming you have /var/www/.scratch/remove-edit-button.php.

drush php:script remove-edit-button --script=path=/var/www/.scratch