FAQ | This is a LIVE service | Changelog

Skip to content
Snippets Groups Projects
Commit 5354e784 authored by Anthony Michaels's avatar Anthony Michaels
Browse files

Merge branch 'main' into config-pantheon

parents 7d3e7a3d 097282a8
No related branches found
No related tags found
No related merge requests found
langcode: en
status: true
status: false
dependencies: { }
id: upgrade_d7_paragraphs_link_block
class: Drupal\migrate\Plugin\Migration
......
services:
cambridge_migrations.commands:
class: \Drupal\cambridge_migrations\Drush\Commands\MigrateTextBlocksCommand
cambridge_migrations.text_blocks_command:
class: Drupal\cambridge_migrations\Drush\Commands\MigrateTextBlocksCommand
arguments: ['@entity_type.manager', '@file_system', '@database']
tags:
- { name: drush.command }
cambridge_migrations.link_blocks_command:
class: Drupal\cambridge_migrations\Drush\Commands\MigrateLinkBlocksCommand
arguments: ['@entity_type.manager', '@file_system', '@database']
tags:
- { name: drush.command }
services:
cambridge_migrations.commands:
cambridge_migrations.text_blocks_command:
class: Drupal\cambridge_migrations\Drush\Commands\MigrateTextBlocksCommand
arguments: ['@entity_type.manager', '@file_system', '@database']
tags:
- { name: drush.command }
cambridge_migrations.link_blocks_command:
class: Drupal\cambridge_migrations\Drush\Commands\MigrateLinkBlocksCommand
arguments: ['@entity_type.manager', '@file_system', '@database']
tags:
- { name: drush.command }
<?php
namespace Drupal\cambridge_migrations\Drush\Commands;
use Drush\Commands\DrushCommands;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Database\Connection;
use Drupal\paragraphs\Entity\Paragraph;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Drush command for migrating D7 Link block paragraphs to D10 Text paragraphs.
*
* Each D7 Link block paragraph has:
* - field_paragraph_heading (plain text, single entry)
* - field_paragraph_links (link field, unlimited entries)
*
* This command converts each D7 Link block into a D10 Text paragraph.
* The resulting content is structured as:
* - If present, the heading is output as an <h2> element.
* - Below the heading, the links are rendered as an unordered list.
*
* To run:
* ddev drush cambridge:migrate-link-blocks
*/
class MigrateLinkBlocksCommand extends DrushCommands {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* Old (source) DB connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $sourceDb;
/**
* New (target) DB connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $targetDb;
/**
* Constructs a new MigrateLinkBlocksCommand object.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
FileSystemInterface $file_system,
Connection $database
) {
parent::__construct();
$this->entityTypeManager = $entity_type_manager;
$this->fileSystem = $file_system;
// Assumes your D7 database connection is configured under the key 'migrate'.
$this->sourceDb = \Drupal\Core\Database\Database::getConnection('default', 'migrate');
$this->targetDb = \Drupal\Core\Database\Database::getConnection('default', 'default');
}
/**
* Clean up HTML content.
*
* @param string $content
* The raw content.
*
* @return string
* The cleaned content.
*/
protected function cleanContent($content) {
if (empty($content)) {
return $content;
}
// Remove empty <p> tags.
$content = preg_replace('/<p>\s*<\/p>/', '', $content);
// Collapse multiple blank lines.
$content = preg_replace('/(\r?\n){2,}/', "\n\n", $content);
return trim($content);
}
/**
* Remove content teaser paragraphs from a node.
*
* This is used to clear out any existing content teasers from a node
* before adding new paragraph items.
*
* @param \Drupal\node\NodeInterface $node
* The node from which to remove content teaser paragraphs.
*/
protected function removeContentTeasers($node) {
if (!$node->hasField('field_paragraph')) {
return;
}
$paragraphs = $node->get('field_paragraph')->referencedEntities();
$updated_paragraphs = [];
$removed_count = 0;
foreach ($paragraphs as $paragraph) {
if ($paragraph->getType() !== 'content_teaser') {
$updated_paragraphs[] = [
'target_id' => $paragraph->id(),
'target_revision_id' => $paragraph->getRevisionId(),
];
}
else {
$paragraph->delete();
$removed_count++;
}
}
if ($removed_count > 0) {
$node->set('field_paragraph', $updated_paragraphs);
$node->save();
$this->logger()->notice(dt('Removed @count content teaser paragraph(s) from node @nid', [
'@count' => $removed_count,
'@nid' => $node->id(),
]));
}
}
/**
* Migrate D7 Link block paragraphs to D10 Text paragraphs.
*
* @command cambridge:migrate-link-blocks
* @aliases cm-link-blocks
*/
public function migrateLinkBlocks() {
try {
// Query the D7 database for Link block paragraphs.
$query = $this->sourceDb->select('paragraphs_item', 'p')
->fields('p', ['item_id', 'bundle', 'field_name'])
->condition('p.bundle', 'link_block');
// Join with the heading field.
$query->leftJoin('field_data_field_paragraph_heading', 'h',
'p.item_id = h.entity_id AND h.entity_type = :entity_type',
[':entity_type' => 'paragraphs_item']
);
$query->fields('h', ['field_paragraph_heading_value']);
// Join with the node reference field to get the parent node ID.
$query->leftJoin('field_data_field_content_items', 'n',
'p.item_id = n.field_content_items_value'
);
$query->fields('n', ['entity_id']);
$link_blocks = $query->execute()->fetchAll();
if (empty($link_blocks)) {
$this->logger()->warning(dt('No link blocks found to migrate.'));
return;
}
$success_count = 0;
$error_count = 0;
foreach ($link_blocks as $block) {
try {
// Skip if no node reference.
if (empty($block->entity_id)) {
$this->logger()->warning(dt('Skipping link block @id - no node reference found', [
'@id' => $block->item_id,
]));
continue;
}
// Start building the content.
$content = '';
if (!empty($block->field_paragraph_heading_value)) {
$content .= '<h2>' . $block->field_paragraph_heading_value . '</h2>';
}
// Query for all links associated with this D7 Link block.
$links_query = $this->sourceDb->select('field_data_field_paragraph_links', 'l')
->fields('l', ['field_paragraph_links_url', 'field_paragraph_links_title'])
->condition('l.entity_id', $block->item_id)
->orderBy('l.delta', 'ASC');
$links = $links_query->execute()->fetchAll();
if (!empty($links)) {
// Render each link as an unordered list.
$links_output = '<ul>';
foreach ($links as $link) {
$url = $link->field_paragraph_links_url;
$title = !empty($link->field_paragraph_links_title) ? $link->field_paragraph_links_title : $url;
$links_output .= '<li><a href="' . $url . '">' . $title . '</a></li>';
}
$links_output .= '</ul>';
$content .= $links_output;
}
// Clean up the final content.
$content = $this->cleanContent($content);
// Load the corresponding D10 node.
$node = $this->entityTypeManager->getStorage('node')->load($block->entity_id);
if (!$node) {
throw new \Exception("Node {$block->entity_id} not found in D10.");
}
// Remove content teasers before adding the new paragraph.
$this->removeContentTeasers($node);
// Create a new paragraph of type 'text' with the generated content.
$paragraph = Paragraph::create([
'type' => 'text',
'field_text' => [
'value' => $content,
'format' => 'filtered_html'
],
]);
$paragraph->save();
// Attach the new paragraph to the node’s field_paragraph.
$node->field_paragraph[] = [
'target_id' => $paragraph->id(),
'target_revision_id' => $paragraph->getRevisionId(),
];
$node->save();
$success_count++;
$this->logger()->notice(dt('Successfully migrated link block @id to node @nid', [
'@id' => $block->item_id,
'@nid' => $block->entity_id,
]));
}
catch (\Exception $e) {
$error_count++;
$this->logger()->error(dt('Error migrating link block @id: @message', [
'@id' => $block->item_id,
'@message' => $e->getMessage(),
]));
}
}
$this->logger()->notice(dt('Migration complete. Successes: @success, Errors: @errors', [
'@success' => $success_count,
'@errors' => $error_count,
]));
}
catch (\Exception $e) {
$this->logger()->error(dt('Migration failed: @message', [
'@message' => $e->getMessage(),
]));
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type_manager'),
$container->get('file_system'),
$container->get('database')
);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment