Learn how to export and import Drupal content, including nodes, images, paragraphs, blocks, and SEO metadata, using the Content Sync module.
Create a custom module named my_custom_export_import
:
my_custom_export_import.info.yml:
name: 'My Custom Export Import'
type: module
description: 'Custom module to export and import nodes with related entities including SEO metadata using Content Sync.'
core_version_requirement: ^8 || ^9
dependencies:
- content_sync:content_sync
- paragraph:paragraphs
- media:media
- block:block
Add the export functionality to my_custom_export_import.module
:
/**
* Implements hook_content_sync_export().
*/
function my_custom_export_import_content_sync_export(&$data, $export_type) {
if ($export_type == 'full') {
// Call the custom export function.
$data['all_entities'] = my_custom_export_all_entities();
}
}
/**
* Custom function to export nodes and their related entities.
*/
function my_custom_export_all_entities() {
$export_data = [];
// Export nodes with related entities.
$export_data['nodes'] = my_custom_export_nodes();
return $export_data;
}
/**
* Custom function to export nodes and their related entities.
*/
function my_custom_export_nodes() {
$nodes = [];
$query = \Drupal::entityQuery('node');
$node_ids = $query->execute();
foreach ($node_ids as $node_id) {
$node = \Drupal\node\Entity\Node::load($node_id);
$node_data = $node->toArray();
// Export related images.
$node_data['images'] = my_custom_export_related_images($node);
// Export related paragraphs.
$node_data['paragraphs'] = my_custom_export_related_paragraphs($node);
// Export related blocks (if applicable).
$node_data['blocks'] = my_custom_export_related_blocks($node);
// Export SEO fields.
$node_data['seo_title'] = $node->get('field_seo_title')->value;
$node_data['seo_description'] = $node->get('field_seo_description')->value;
$node_data['seo_keywords'] = $node->get('field_seo_keywords')->value;
$nodes[] = $node_data;
}
return $nodes;
}
/**
* Custom function to export related images for a node.
*/
function my_custom_export_related_images($node) {
$images = [];
foreach ($node->getFields() as $field_name => $field) {
if ($field->getFieldDefinition()->getType() == 'image') {
foreach ($field->getValue() as $item) {
$file = \Drupal\file\Entity\File::load($item['target_id']);
$images[] = [
'id' => $file->id(),
'uri' => $file->getFileUri(),
];
}
}
}
return $images;
}
/**
* Custom function to export related paragraphs for a node.
*/
function my_custom_export_related_paragraphs($node) {
$paragraphs = [];
foreach ($node->getFields() as $field_name => $field) {
if ($field->getFieldDefinition()->getType() == 'entity_reference_revisions') {
foreach ($field->getValue() as $item) {
$paragraph = \Drupal\paragraphs\Entity\Paragraph::load($item['target_id']);
$paragraphs[] = $paragraph->toArray();
}
}
}
return $paragraphs;
}
/**
* Custom function to export related blocks for a node (if applicable).
*/
function my_custom_export_related_blocks($node) {
// Adjust according to how blocks are used in your site.
$blocks = [];
// Logic to export blocks if applicable.
return $blocks;
}
Add the import functionality to my_custom_export_import.module
:
/**
* Implements hook_content_sync_import().
*/
function my_custom_export_import_content_sync_import(&$data, $import_type) {
if ($import_type == 'full') {
// Call the custom import function.
my_custom_import_all_entities($data['all_entities']);
}
}
/**
* Custom function to import all entities.
*/
function my_custom_import_all_entities($data) {
// Import nodes and their related entities.
if (isset($data['nodes'])) {
my_custom_import_nodes($data['nodes']);
}
}
/**
* Function to import nodes and their related entities.
*/
function my_custom_import_nodes($nodes) {
foreach ($nodes as $node_data) {
// Handle images and paragraphs before creating the node to ensure references are correct.
if (isset($node_data['images'])) {
my_custom_import_related_images($node_data['images']);
}
if (isset($node_data['paragraphs'])) {
my_custom_import_related_paragraphs($node_data['paragraphs']);
}
if (isset($node_data['blocks'])) {
my_custom_import_related_blocks($node_data['blocks']);
}
// Import the node itself.
// Ensure you handle the creation of the node with necessary checks and updates.
$node = \Drupal\node\Entity\Node::create($node_data);
// Import SEO fields.
if (isset($node_data['seo_title'])) {
$node->set('field_seo_title', $node_data['seo_title']);
}
if (isset($node_data['seo_description'])) {
$node->set('field_seo_description', $node_data['seo_description']);
}
if (isset($node_data['seo_keywords'])) {
$node->set('field_seo_keywords', $node_data['seo_keywords']);
}
$node->save();
}
}
/**
* Function to import related images.
*/
function my_custom_import_related_images($images) {
foreach ($images as $image_data) {
// Check if the file already exists to avoid duplicates.
$existing_file = \Drupal\file\Entity\File::load($image_data['id']);
if (!$existing_file) {
$file = \Drupal\file\Entity\File::create([
'uri' => $image_data['uri'],
'status' => 1,
]);
$file->save();
}
}
}
/**
* Function to import related paragraphs.
*/
function my_custom_import_related_paragraphs($paragraphs) {
foreach ($paragraphs as $paragraph_data) {
// Check if the paragraph already exists to avoid duplicates.
$existing_paragraph = \Drupal\paragraphs\Entity\Paragraph::load($paragraph_data['id']);
if (!$existing_paragraph) {
$paragraph = \Drupal\paragraphs\Entity\Paragraph::create($paragraph_data);
$paragraph->save();
}
}
}
/**
* Function to import related blocks (if applicable).
*/
function my_custom_import_related_blocks($blocks) {
// Adjust according to your block usage.
foreach ($blocks as $block_data) {
// Logic to import blocks, if applicable.
}
}
Create a controller to handle the UI and form submissions for export and import:
src/Controller/ExportImportController.php:
namespace Drupal\my_custom_export_import\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
class ExportImportController extends ControllerBase {
/**
* Export content.
*/
public function exportContent() {
// Trigger the export.
$data = [];
my_custom_export_import_content_sync_export($data, 'full');
// Convert to JSON and download.
$json_data = json_encode($data['all_entities'], JSON_PRETTY_PRINT);
$response = new Response($json_data);
$response->headers->set('Content-Type', 'application/json');
$response->headers->set('Content-Disposition', 'attachment; filename="exported_content.json"');
return $response;
}
/**
* Import content.
*/
public function importContent(Request $request) {
// Handle file upload.
$file = $request->files->get('import_file');
if ($file && $file->isValid()) {
$data = json_decode(file_get_contents($file->getPathname()), TRUE);
if (isset($data['all_entities'])) {
my_custom_import_all_entities($data['all_entities']);
return new JsonResponse(['status' => 'Import successful!']);
}
return new JsonResponse(['status' => 'Invalid data format.'], 400);
}
return new JsonResponse(['status' => 'No file uploaded.'], 400);
}
}
Define routes in my_custom_export_import.routing.yml
:
my_custom_export_import.export:
path: '/admin/export-content'
defaults:
_controller: '\Drupal\my_custom_export_import\Controller\ExportImportController::exportContent'
_title: 'Export Content'
requirements:
_permission: 'administer site configuration'
my_custom_export_import.import:
path: '/admin/import-content'
defaults:
_controller: '\Drupal\my_custom_export_import\Controller\ExportImportController::importContent'
_title: 'Import Content'
requirements:
_permission: 'administer site configuration'
methods: [POST]
Create a simple form to handle export and import in your theme or as a block:
templates/export_import_form.html.twig:
<div class="export-import-form">
<h2>Export Content</h2>
<form action="/admin/export-content" method="get">
<button type="submit">Export</button>
</form>
<h2>Import Content</h2>
<form action="/admin/import-content" method="post" enctype="multipart/form-data">
<input type="file" name="import_file" required />
<button type="submit">Import</button>
</form>
</div>
References:
Published By: Krishanu Jadiya
Updated at: 2024-07-25 15:56:14