Сиротские абзацы в Drupal 8

Я заметил, что сущности Paragraph не удаляются из базы данных. Они скорее отключаются от родительского узла.

Это заметно, если вы создаете представление, в котором перечислены абзацы и прикрепляете контекстный фильтр, который фильтрует по Parent ID.

На данный момент я нашел обходной путь, чтобы создать представление со списком Content. Присоединить отношение к абзацу. Таким образом обеспечивается отображение только связанных абзацев.

По-прежнему существует проблема, связанная с наличием в базе данных сотен потерянных абзацев и полевых данных. Есть ли способ их очистить?

Обновлено: По-видимому, это серьезная ошибка, которую можно найти в системе отслеживания проблем модуля Paragraph: Удаленные объекты абзаца не удаляются из базы данных

Неужели весь абзац? Или только его ID?

leymannx 25.04.2018 13:15

Исходя из существующей проблемы, может показаться, что весь абзац: drupal.org/project/paragraphs/issues/2741937

Elaman 25.04.2018 13:46
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Разработка Drupal и AngularJS: Идеальное сочетание для вашей веб-стратегии
Разработка Drupal и AngularJS: Идеальное сочетание для вашей веб-стратегии
Один опытный веб-разработчик назвал комбинацию Drupal и AngularJS "сочетанием, созданным на небесах". Почему так? Потому что вместе они могут создать...
6
2
4 356
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Теперь, когда я понял, что это ошибка и она еще не исправлена, моя главная цель - просто очистить ненужные абзацы.

Кто-то создал этот модуль: Абзац чистый, но я не фанат использования модулей для таких целей.

Итак, ниже я опубликую свою первую успешную попытку решить эту проблему. Я должен предупредить это небезопасно, потому что он удаляет абзацы!

Решение не тестировалось на использование редакций, перевод контента и т. д. Так что это может испортить вам день. Сделайте резервную копию вашего сайта.

Используя модуль Devel, перейдите в Development> Execute PHP Code. Вставьте и выполните следующий код:

// get all paragraphs
$deleted = [];
$paragraph_ids = \Drupal::entityQuery('paragraph')->execute();
$paragraphs = \Drupal::entityTypeManager()->getStorage('paragraph')->loadMultiple($paragraph_ids);
foreach ($paragraphs as $target_id => $paragraph) {
  // get parent entity (node, taxonomy, paragraph, etc.)
  $parent = $paragraph->getParentEntity();
  $field_name = $paragraph->parent_field_name->value;

  // Check if current paragraph exists in parent entity field values
  $exists = FALSE;
  $values = is_null($parent) ? [] : $parent->get($field_name)->getValue();
  foreach($values as $value) {
    if ($value['target_id'] == $target_id) {
      $exists = TRUE;
    }
  }

  // Delete paragraphs that aren't linked to an entity they claim as a parent
  if (!$exists) {
    $paragraph->delete();
    $deleted[] = $target_id;
  }
}

print "Deleted paragraph IDs: " . implode(', ', $deleted);

Я обнаружил, что модуль очистки абзацев удаляет только недавно удаленные абзацы, поэтому, если у вас есть потерянные абзацы, вам также необходимо использовать это решение. Кроме того, функция «Выполнение PHP» теперь удалена из модуля Devel, но вы можете вернуть ее, установив Devel PHP (devel_php).

SKWebDev 15.10.2020 21:05

Мы создали модуль под названием paragraphs_clean, который реализует hook_entity_update () (будьте осторожны: имя модуля должно быть равно префиксу функции перехвата entity_update ()), вдохновившись здесь: https://www.drupal.org/project/paragraphs/issues/2741937#comment-13181377.

Как утверждается, он работает хорошо, даже если есть исправления и переводы. Также нам пришлось адаптировать исходный код php для работы в случае, когда абзацы тоже имеют абзацы (т.е. вложенные абзацы).

Этот код более эффективен, чем приведенный выше, поскольку он загружает только потерянные абзацы объекта из контекста.

Вот код:

<?php


/**
* @file
* Paragraphs clean module.
*/

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\field\Entity\FieldConfig;

/**
* Implements hook_entity_update().
* 
* When form updates, delete any paragraph entities that were removed.
* 
* @param \Drupal\Core\Entity\EntityInterface $entity
*/
function paragraphs_clean_entity_update(EntityInterface $entity) {

  // Only act on content entities.
  if (!($entity instanceof FieldableEntityInterface)) {
    return;
  }

  $fieldManager = \Drupal::service('entity_field.manager');
  $parentEntities = $fieldManager->getFieldMapByFieldType('entity_reference_revisions');

  if (!array_key_exists($entity->getEntityTypeId(), $parentEntities)) {
    return;
  }

  $paragraph_definitions = [];

  // loop through all paragraph types
  foreach ($parentEntities[$entity->getEntityTypeId()] as $field_id => $settings) {
    if ($configField = FieldConfig::loadByName($entity->getEntityTypeId(), $entity->bundle(), $field_id)) {
      $paragraph_definitions[] = $configField;
    }
  }

  if (empty($paragraph_definitions)) {
    return;
  }

  foreach ($paragraph_definitions as $paragraph_definition) {

    //get entity type name to make it work with any kind of parent entity (node, paragraph, etc.)
    $entityTypeName = $entity->getEntityTypeId();

    // Remove orphaned paragraphs.
    $query = \Drupal::database()->select('paragraphs_item_field_data', 'pfd')
      ->fields('pfd', ['id'])
      ->condition('pfd.parent_type', $entityTypeName) 
      ->condition('pfd.parent_id', $entity->id())
      ->condition('pfd.parent_field_name', $paragraph_definition->getName());

    $query->addJoin('left', $entityTypeName.'__'.$paragraph_definition->getName(),'nt','pfd.id=nt.'.$paragraph_definition->getName().'_target_id');
    $query->isNull('nt.'.$paragraph_definition->getName().'_target_id');
    $query->distinct();

    $paragraph_ids = $query->execute()->fetchCol();

    if ($paragraph_ids) {
      $para_storage = \Drupal::entityTypeManager()->getStorage('paragraph');
      foreach ($paragraph_ids as $paragraph_id) {

        if ($para = $para_storage->load($paragraph_id)) {
          $para->delete();
          drupal_set_message(t('Paragraph of type "%type" has been deleted: %id', ['%id' => $paragraph_id, '%type' => $paragraph_definition->getName()]));
        }

      }
    }   

  }

}

Другие вопросы по теме