Infinite loop in hook_entity_storage_load

Created on 15 February 2024, 10 months ago

Problem/Motivation

hook_entity_storage applies on all field and should apply only on image field.
In my case, the hook is called on metatag field, it calls the getValue() method on that field and that makes the entity to be loaded again and makes an infinite loop.

Proposed resolution

I'll purpose a patch that fixes it.

πŸ› Bug report
Status

Active

Version

2.0

Component

Code

Created by

πŸ‡«πŸ‡·France Quentin Massez

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @Quentin Massez
  • Status changed to Needs review 10 months ago
  • πŸ‡«πŸ‡·France Quentin Massez

    Update, I still have infinite loop with my patch because of the entity->getFields(). We dont need to load all fields because we juste need fields name to get caption on it.
    Since the last time, i saw that there is a function _image_field_caption_get_image_field_names to get the image field name, it's called in hook entity update and should also be called in hook entity storage load.

    Here is the new patch.

  • First commit to issue fork.
  • πŸ‡ΊπŸ‡¦Ukraine Foxy-vikvik

    On Drupal 9.3 works fine and fixed the main issue related to the infinity loop. I had infinity loop when generate Sitemaps using module Simple SiteMaps.

  • πŸ‡ΊπŸ‡¦Ukraine Foxy-vikvik

    The best option is to remove hook_entity_storage_load and replace with the custom logic. It has bad impact on the sit performace.

  • Merge request !15Resolve #3421670 "Infinite loop in" β†’ (Open) created by Foxy-vikvik
  • Pipeline finished with Success
    3 months ago
    Total: 132s
    #297810
  • Pipeline finished with Success
    3 months ago
    Total: 136s
    #297812
  • πŸ‡©πŸ‡ͺGermany Bruno2

    In Drupal 10 (with more than 8k nodes), every page that uses hook_entity_storage_load runs infinite loops. Therefore /admin/content is not available. Cron cannot finish its run and nodes cannot be deleted via the Drupal API. The patches don't help, so it must be limeted to the current node, without load all entities. I dont think that hook_entity_storage_load is the best hook to set or get the values for this field.

  • πŸ‡©πŸ‡ͺGermany Bruno2

    Ok, here is a small fix up, please check for the values. I tested it only a little bit:

    Rewrite function to get display the values on edit nodes:

    function _image_field_caption_widget_process($element, &$form_state, $form) {
       $entity = NULL;
      $form_object = $form_state->getFormObject();
      if ($form_object instanceof EntityFormInterface) {
        $entity = $form_object->getEntity();
      }
    
      if (!$entity) {
        return $element;
      }
    
      // Get caption from storage
      $caption = \Drupal::service('image_field_caption.storage')->getCaption(
        $entity->getEntityTypeId(),
        $entity->bundle(),
        $element['#field_name'],
        $entity->id(),
        $entity->getRevisionId(),
        $entity->language()->getId(),
        $element['#delta']
      );
    
      // Add the caption field to the widget
      $element['image_field_caption'] = [
        '#type' => 'text_format',
        '#title' => t('Caption'),
        '#default_value' => $caption['caption'] ?? '',
        '#format' => $caption['caption_format'] ?? 'plain_text',
        '#weight' => 10,
        '#required' => $element['#caption_field_required'],
        '#access' => (bool) $element['#value']['fids'],
        '#element_validate' => $element['#caption_field_required'] ? ['_image_field_caption_validate_required'] : [],
      ];
    
      return $element;
    }
    

    Delete the whole function:

    function image_entity_storage_load_field_caption(array $entities, $entity_type_id) {}
    

    And insert this for it:

    function image_field_caption_preprocess_field(&$variables) {
      // Only process image fields
      if ($variables['element']['#field_type'] !== 'image') {
        return;
      }
    
      $entity = $variables['element']['#object'];
      $field_name = $variables['element']['#field_name'];
      $entity_type = $entity->getEntityTypeId();
      $bundle = $entity->bundle();
      $entity_id = $entity->id();
      $revision_id = $entity->getRevisionId();
      $langcode = $entity->language()->getId();
    
      foreach ($variables['items'] as $delta => &$item) {
        if (!isset($item['content']['#item'])) {
          continue;
        }
    
        // Get caption using the actual storage service method
        $caption = \Drupal::service('image_field_caption.storage')
          ->getCaption(
            $entity_type,
            $bundle,
            $field_name,
            $entity_id,
            $revision_id,
            $langcode,
            $delta
          );
    
        if (!empty($caption['caption'])) {
          $item['content']['caption'] = [
            '#type' => 'markup',
            '#markup' => $caption['caption'],
            '#format' => $caption['caption_format'],
            '#weight' => 10,
          ];
        }
      }
    }
    

    image_field_caption_entity_insert and image_field_caption_entity_update should also be handled in the widget.

    **
     * Implements hook_form_FORM_ID_alter()
     */
    function MODULE_NAME_form_FORM_ID_alter(&$form, $form_state, $form_id) {
    
      $form['submit']['#submit'][] = 'custom_submit_function';
    }
    
    function custom_submit_function($form, $form_state) {
      ....
    }
    
Production build 0.71.5 2024