'Drupal 8 orphan paragraphs
I've noticed that Paragraph entities are not deleted from the database. They rather unlink from the parent node.
This is noticeable if you create a view, that lists Paragraphs and attach a contextual filter, that filters by Parent ID.
For now, I've found a workaround, to create a view that lists Content. Attach relationship to a Paragraph. This way it ensures that only linked paragraphs are displayed.
There is still an issue of having hundreds of orphan Paragraphs and field data in the database. Is there a way of cleaning them?
EDIT: This is apparently a major bug and could be found in Paragraph module's issue tracker: Removed paragraph entities are not deleted from database
Solution 1:[1]
We created a module called paragraphs_clean that implements hook_entity_update() (be careful: module name must be equal to the prefix of the entity_update() hook function) taking inspiration from here: https://www.drupal.org/project/paragraphs/issues/2741937#comment-13181377.
As it is stated, it seem to work well even if there are revisions and translations. Also we had to adapt the original php code to function in the case when paragraphs have paragraphs, too (i.e. nested paragraphs).
This code is more efficient than the above as it will only load orphan paragraphs of the entity from the context.
Here is the code:
<?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()]));
}
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
