Skip to content

Instantly share code, notes, and snippets.

@AJV009
Last active July 6, 2022 06:22
Show Gist options
  • Select an option

  • Save AJV009/a0f06885a2a8db632be18e99b753b67c to your computer and use it in GitHub Desktop.

Select an option

Save AJV009/a0f06885a2a8db632be18e99b753b67c to your computer and use it in GitHub Desktop.
List nodes using same paragraph data items with but with different revisions.
<?php
/**
* Stage 1: Evaluation / Analysis of nodes using same the paragraph data tiem with different revisions.
*/
function analysis($print_output = false, $print_count = 20) {
if ($print_output) print("\nStage 1: Analyze the messed up paragraph data: ------------------------------------------------\n");
$database = \Drupal::database();
/**
* Fetch all the paragraphs data from paragraph_item_revision_field_data table.
* Only selecting the latest revisions.
*/
$revision_item_data = $database->query('SELECT p.id, p.parent_id, p.parent_type, p.parent_field_name, max(p.revision_id) as max_revision_id
FROM {paragraphs_item_revision_field_data} p
GROUP BY p.id, p.parent_id, p.parent_type, p.parent_field_name
ORDER BY p.id, p.parent_id, p.parent_type, p.parent_field_name, max_revision_id')->fetchAll();
// Convert to array.
$revision_item_data = array_map(function($item) {
return [
'id' => $item->id,
'parent_id' => $item->parent_id,
'parent_type' => $item->parent_type,
'parent_field_name' => $item->parent_field_name,
'revision_id' => $item->max_revision_id,
];
}, $revision_item_data);
// sort revision item data by id
usort($revision_item_data, function($a, $b) {
return $a['id'] - $b['id'];
});
/**
* Fetch all the paragraphs data from paragraph_item_field_data table.
*/
$item_data = $database->query('SELECT p.id, p.parent_id, p.parent_type, p.revision_id, p.parent_field_name
FROM {paragraphs_item_field_data} p')->fetchAll();
// Convert to array.
$item_data = array_map(function($item) {
return [
'id' => $item->id,
'parent_id' => $item->parent_id,
'parent_type' => $item->parent_type,
'parent_field_name' => $item->parent_field_name,
'revision_id' => $item->revision_id,
];
}, $item_data);
// sort revision item data by id
usort($item_data, function($a, $b) {
return $a['id'] - $b['id'];
});
/**
* Analyse the data and find the duplicate data.
* Sorting duplicates and creating a array of use data.
*/
// Analysis helper variables.
$next_loop_id = 0;
$messed_data = [];
$tot_others = 0;
$unique_parents = [];
// Loop through the data from paragraph_item_data
foreach ($item_data as $k => $item) {
// Variables to store active and duplicate parent data.
$current = [];
$others = [];
// Loop through the data from paragraph_item_revision_field_data.
// Slicing data from the current loop. to save time because we are looping through the same data which is sorted by id
// This is id was already used to sort the data.
foreach (array_slice($revision_item_data, $next_loop_id, null, true) as $l => $revision) {
if ($item['id'] == $revision['id']) {
// Find the item which is actiely owning the paragraph data.
if ($item['parent_id'] == $revision['parent_id']
&& $item['parent_type'] == $revision['parent_type']
&& $item['revision_id'] == $revision['revision_id']) {
$current = $revision;
}
// Rest of them with the same id are the duplicates.
else {
$others[] = $revision;
}
// Slicing array for performace
$next_loop_id = $l;
}
}
// Only store if the data if there is an active parent and at least one duplicate.
// There is often data with no active parent.
if(count($current) > 0 && count($others) > 0) {
// Store the data.
$messed_data[] = [
'current' => $current,
'others' => $others,
];
// tracking total duplicates
$tot_others += (count($others) + 1);
// Storing parents from active and duplicate data.
foreach ($others as $other) {
if ($other['parent_type'] == 'node') {
$unique_parents[] = [
'parent_id' => $other['parent_id'],
'parent_type' => $other['parent_type'],
];
}
}
if ($current['parent_type'] == 'node') {
$unique_parents[] = [
'parent_id' => $current['parent_id'],
'parent_type' => $current['parent_type'],
];
}
}
}
// Reorganize the data.
$messed_data = [
'total_cloned_entities' => $tot_others,
'unique_main_entity' => array_unique($unique_parents, SORT_REGULAR),
'data' => array_values($messed_data),
];
// Print the data if requested via print parameter.
if ($print_output) {
print("Total messed items: " . $tot_others . "\n");
print("Unique main entities: " . count($messed_data['unique_main_entity']) . "\n");
print("\nUnique Node Entities: ------------------------------------------------\n");
print_r($messed_data['unique_main_entity']);
print("\nMessed Data: ------------------------------------------------\n");
foreach ($messed_data['data'] as $k => $item) {
print_r($item);
if ($print_count != 0 && $k < $print_count) {
break;
}
}
}
// Return the data for futher use.
return $messed_data;
}
/**
* Stage 2: Update and Fix the data.
*/
function fix($messed_data = [], $print_output = false) {
if ($print_output) print("\nStage 2: Update and Fix the data: ------------------------------------------------\n");
// Only store the data if there is any.
$messed_data = $messed_data['data'];
// loop through the messed data
foreach ($messed_data as $k => $messed_data_item) {
// Print progress.
if ($print_output) print("\n Progress: " . ($k + 1) . "/" . count($messed_data) . "\n");
// Loop through the duplicates. Since we dont want to touch the active data and mess with it.
foreach ($messed_data_item['others'] as $other) {
// Load the data with duplicate parent.
$data = \Drupal::entityTypeManager()->getStorage('paragraph')->loadRevision($other['revision_id']);
if ($data) {
// find parent data from $data
$parent_data = \Drupal::entityTypeManager()->getStorage($other['parent_type'])->load($other['parent_id']);
if ($parent_data) {
// find $data from $parent_data
foreach ($parent_data->getFieldDefinitions() as $field_name => $field_definition) {
$data_change = false;
$field_value = $parent_data->get($field_name)->getValue();
foreach ($field_value as $j => $v) {
// check if $v has target_id and target_revision_id key
if (isset($v['target_id']) && isset($v['target_revision_id'])) {
if ($v['target_id'] == $data->id() && $v['target_revision_id'] == $data->getRevisionId()) {
// When the field is found in the parent entity.
// Clone the data and reassign it to make it an active parent with no other dependency on the data.
$new_p_data = $data->createDuplicate();
$new_p_data->setParentEntity($parent_data, $other['parent_field_name']);
$new_p_data->save();
$field_value[$j]['target_id'] = $new_p_data->id();
$field_value[$j]['target_revision_id'] = $new_p_data->getRevisionId();
$data_change = true;
if ($print_output) print("Updated:" . $other['parent_type'] . " " . $parent_data->id() . " - " . $field_name . " replaced " . $data->getRevisionId() . " with " . $new_p_data->getRevisionId() . "\n");
// delete $data paragraph revision
$data->delete();
if ($print_output) print("Deleted:" . $other['revision_id'] . "\n");
}
}
}
if ($data_change) {
// save the new field value
$parent_data->set($field_name, $field_value);
$parent_data->save();
}
}
}
else {
$data->delete();
if ($print_output) print("Deleted:" . $other['revision_id'] . "\n");
}
}
else {
// remove this data from the table, since the paragraph doesn't exist anymore.
}
}
}
}
// Main to fetch and fix!
// replace the print parameter with true from false to print any output to the screen.
$messed_data = analysis(true,0);
if (count($messed_data['data']) > 0) {
fix($messed_data, true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment