Last active
July 6, 2022 06:22
-
-
Save AJV009/a0f06885a2a8db632be18e99b753b67c to your computer and use it in GitHub Desktop.
List nodes using same paragraph data items with but with different revisions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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