Skip to content

Instantly share code, notes, and snippets.

@edutrul
Last active June 21, 2024 16:23
Show Gist options
  • Save edutrul/aef8397a45469747217fe6ff6b860cc8 to your computer and use it in GitHub Desktop.
Save edutrul/aef8397a45469747217fe6ff6b860cc8 to your computer and use it in GitHub Desktop.
Easy Cascade CMS migration so next CMU developers can extend it.

Custom Cascade Migration

This custom Drupal module provides functionality to recursively fetch pages from a fake endpoint and store them into the cascade_pages database table. It includes services for handling HTTP requests, storing data, and managing recursive fetching.

Features

  • Recursively fetch pages from a specified endpoint.
  • Store fetched pages into the cascade_pages database table.
  • Logging of fetch and storage operations.

Installation

  1. Place the custom_migration module in the modules/custom directory of your Drupal installation.

  2. Enable the module using Drush:

    drush en custom_migration
  3. Run the command

    drush cmu_cascade:fetch-pages

    Wait and you'll get all content migrated successfully.

How to extend?

To extend the functionality of this module, you can add new services or modify existing ones. Take for example the existing migration class CMULandingPageMigrationService, change its name to for example CMUBlogPostMigrationService and make sure in the factory class you call for instance:

  public function getService($page_type) {
    switch ($page_type) {
      case 'article':
        return new CMUArticleMigrationService($this->logger);
      case 'landing_page':
        return new CMULandingPageMigrationService($this->logger);
      // HERE GOES YOUR CUSTOM CLASS FOR A NEW MIGRATION.
      case 'blog'
        return new CMUBlogPostMigrationService($this->logger);
      // Add more cases for different page types.
    }
  }

Need more help?

<?php
// File: src/Commands/CascadeMigrationCommands.php
<?php
namespace Drupal\cmu_cascade\Commands;
use Drush\Commands\DrushCommands;
use Drupal\cmu_cascade\Service\CascadeCMS;
/**
* A Drush command file for fetching pages recursively.
*/
final class FetchPagesCommand extends DrushCommands {
/**
* The page loader service.
*
* @var \Drupal\cmu_cascade\Service\CascadeCMS
*/
protected $cascadeCMS;
/**
* Constructs a FetchPagesCommand object.
*
* @param \Drupal\cmu_cascade\Service\CascadeCMS $cascadeCMS
* The cascade CMS service.
*/
public function __construct(CascadeCMS $cascadeCMS) {
$this->cascadeCMS = $cascadeCMS;
}
/**
* Fetch pages recursively from a fake endpoint.
*
* @command cmu_cascade:fetch-pages
* @aliases cpfp
*/
public function fetchPages() {
$url = 'https://fake-endpoint.com/api/pages';
$results = $this->cascadeCMS->fetchPagesRecursive($url);
$this->logger()->info('Fetched @count pages', ['@count' => count($results)]);
}
}
<?php
// File: src/Service/CascadeMigrationService.php
namespace Drupal\cmu_cascade\Service;
use Drupal\Core\Database\Connection;
use Psr\Log\LoggerInterface;
/**
* Class CascadeMigrationService.
*
* Service for migrating pages from Cascade CMS to Drupal.
*/
class CascadeMigrationService {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The page checker service.
*
* @var \Drupal\cmu_cascade\Service\PageCheckerService
*/
protected $pageCheckerService;
/**
* The page migration factory.
*
* @var \Drupal\cmu_cascade\Service\PageMigrationFactory
*/
protected $pageMigrationFactory;
/**
* Constructs a CascadeMigrationService object.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\cmu_cascade\Service\PageCheckerService $pageCheckerService
* The page checker service.
* @param \Drupal\cmu_cascade\Service\PageMigrationFactory $pageMigrationFactory
* The page migration factory.
*/
public function __construct(Connection $database, PageCheckerService $pageCheckerService, PageMigrationFactory $pageMigrationFactory) {
$this->database = $database;
$this->pageCheckerService = $pageCheckerService;
$this->pageMigrationFactory = $pageMigrationFactory;
}
/**
* Migrates pages from Cascade CMS to Drupal.
*/
public function migratePages() {
$pages = $this->fetchCascadePages();
foreach ($pages as $page) {
$this->migratePage($page);
}
$this->pageMigrationFactory->getLogger()->info('Migration completed successfully.');
}
/**
* Fetches all pages from Cascade CMS.
*
* @return array
* An array of pages from Cascade CMS.
*/
protected function fetchCascadePages() {
$query = $this->database->select('cascade_pages', 'cp')
->fields('cp', ['id', 'title', 'content', 'page_type', 'entity_id']);
return $query->execute()->fetchAllAssoc('id');
}
/**
* Migrates a single page from Cascade CMS to Drupal.
*
* @param object $page
* The page object from Cascade CMS.
*/
protected function migratePage($page) {
if ($this->pageCheckerService->pageExists($page->title)) {
return;
}
$pageMigrationService = $this->pageMigrationFactory->getService($page->page_type);
$pageMigrationService->createPage($page);
}
}
services:
cmu_cascade.page_checker:
class: Drupal\cmu_cascade\Service\PageCheckerService
arguments: ['@entity_type.manager']
cmu_cascade.page_creator:
class: Drupal\cmu_cascade\Service\PageCreatorService
arguments: ['@logger.channel.default']
cmu_cascade.page_migration_factory:
class: Drupal\cmu_cascade\Service\PageMigrationFactory
arguments: ['@logger.channel.default']
cmu_cascade.cascade_migration:
class: Drupal\cmu_cascade\Service\CascadeMigrationService
arguments: ['@database', '@cmu_cascade.page_checker', '@cmu_cascade.page_migration_factory']
cmu_cascade.data_storage:
class: Drupal\cmu_cascade\Service\DataStorageService
arguments: ['@database', '@logger.channel.default']
cmu_cascade.cascade_cms:
class: Drupal\cmu_cascade\Service\CascadeCMS
arguments: ['@http_client', '@cmu_cascade.data_storage', '@logger.channel.default']
<?php
// File: src/Service/CMUArticleMigrationService.php
namespace Drupal\cmu_cascade\Service;
use Drupal\node\Entity\Node;
use Psr\Log\LoggerInterface;
/**
* Class CMUArticleMigrationService.
*
* Service for migrating articles from Cascade CMS to Drupal.
*/
final class CMUArticleMigrationService implements PageMigrationInterface {
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a CMUArticleMigrationService object.
*
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
*/
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function createPage($page) {
$node = Node::create([
'type' => 'article',
'title' => $page->title,
'body' => [
'value' => $page->content,
'format' => 'full_html',
],
'field_image' => $page->image,
'field_article_types' => $page->article_types,
]);
$node->save();
$this->logger->info('Migrated article: ' . $page->title);
}
}
<?php
// File: src/Service/CMULandingPageMigrationService.php
namespace Drupal\cmu_cascade\Service;
use Drupal\node\Entity\Node;
use Psr\Log\LoggerInterface;
/**
* Class CMULandingPageMigrationService.
*
* Service for migrating landing pages from Cascade CMS to Drupal.
*/
class CMULandingPageMigrationService implements PageMigrationInterface {
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a CMULandingPageMigrationService object.
*
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
*/
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function createPage($page) {
$node = Node::create([
'type' => 'landing_page',
'title' => $page->title,
'field_section' => $page->section,
'field_header' => $page->header,
'field_intro_text' => $page->intro_text,
'field_video' => $page->video,
]);
$node->save();
$this->logger->info('Migrated landing page: ' . $page->title);
}
}
<?php
namespace Drupal\cmu_cascade\Service;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
/**
* Class CascadeCMS.
*
* Service for recursively fetching pages from a fake endpoint.
*/
class CascadeCMS {
/**
* The HTTP client.
*
* @var \GuzzleHttp\Client
*/
protected $httpClient;
/**
* The data storage service.
*
* @var \Drupal\cmu_cascade\Service\DataStorageService
*/
protected $dataStorageService;
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a FetchCascadeCMSDataService object.
*
* @param \GuzzleHttp\Client $httpClient
* The HTTP client.
* @param \Drupal\cmu_cascade\Service\DataStorageService $dataStorageService
* The data storage service.
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
*/
public function __construct(Client $httpClient, DataStorageService $dataStorageService, LoggerInterface $logger) {
$this->httpClient = $httpClient;
$this->dataStorageService = $dataStorageService;
$this->logger = $logger;
}
/**
* Fetches pages recursively from the given endpoint.
*
* @param string $url
* The URL to fetch pages from.
* @param array $results
* The accumulated results.
*
* @return array
* The fetched pages.
*/
public function fetchPagesRecursive($url, &$results = []) {
try {
$response = $this->httpClient->get($url);
$data = json_decode($response->getBody(), TRUE);
// Process the current page's data
if (!empty($data['pages'])) {
foreach ($data['pages'] as $page) {
$results[] = $page;
// Store the page data
$this->dataStorageService->storePage($page);
// If the page has children, fetch them recursively
if (!empty($page['children_url'])) {
$this->fetchPagesRecursive($page['children_url'], $results);
}
}
}
return $results;
} catch (\Exception $e) {
$this->logger->error('Error fetching pages: @message', ['@message' => $e->getMessage()]);
return $results;
}
}
}
<?php
// File: src/Service/PageMigrationFactory.php
namespace Drupal\cmu_cascade\Service;
use Psr\Log\LoggerInterface;
/**
* Class PageMigrationFactory.
*
* Factory for creating page migration services based on page type.
*/
final class PageMigrationFactory {
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a PageMigrationFactory object.
*
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
*/
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* Gets the appropriate page migration service based on page type.
*
* @param string $page_type
* The type of the page.
*
* @return \Drupal\cmu_cascade\Service\PageMigrationInterface
* The page migration service.
*/
public function getService($page_type) {
switch ($page_type) {
case 'article':
return new CMUArticleMigrationService($this->logger);
case 'landing_page':
return new CMULandingPageMigrationService($this->logger);
// Add more cases for different page types.
default:
throw new \InvalidArgumentException('Unsupported page type: ' . $page_type);
}
}
}
<?php
// File: src/Service/PageMigrationInterface.php
namespace Drupal\cmu_cascade\Service;
/**
* Interface PageMigrationInterface.
*
* Interface for migrating pages from Cascade CMS to Drupal.
*/
interface PageMigrationInterface {
/**
* Creates a Drupal node for the migrated page.
*
* @param object $page
* The page object from Cascade CMS.
*/
public function createPage($page);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment