devel_wizard-2.x-dev/src/Plugin/DevelWizard/Spell/ConfigEntityMigrationSpellBase.php
src/Plugin/DevelWizard/Spell/ConfigEntityMigrationSpellBase.php
<?php
declare(strict_types=1);
namespace Drupal\devel_wizard\Plugin\DevelWizard\Spell;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Utility\Random;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\devel_wizard\ShellProcessFactoryInterface;
use Drupal\devel_wizard\Spell\SpellManagerInterface;
use Drupal\devel_wizard\Spell\SpellTraitPackageManager;
use Drupal\devel_wizard\Utils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
abstract class ConfigEntityMigrationSpellBase extends ConfigEntitySpellBase {
use SpellTraitPackageManager;
protected SpellManagerInterface $spellManager;
protected TwigEnvironment $twig;
protected UuidInterface $uuid;
protected string $sitePath;
protected Random $random;
protected Filesystem $fs;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger'),
$container->get('logger.channel.devel_wizard_spell'),
$container->get('string_translation'),
$container->get('devel_wizard.utils'),
$container->get('entity_type.manager'),
$container->get('config.factory'),
$container->get('plugin.manager.devel_wizard.spell'),
$container->get('devel_wizard.shell_process_factory'),
$container->get('extension.list.module'),
$container->get('module_installer'),
$container->get('twig'),
$container->get('uuid'),
$container->getParameter('site.path'),
);
}
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MessengerInterface $messenger,
LoggerInterface $logger,
TranslationInterface $stringTranslation,
Utils $utils,
EntityTypeManagerInterface $entityTypeManager,
ConfigFactoryInterface $configFactory,
SpellManagerInterface $spellManager,
ShellProcessFactoryInterface $shellProcessFactory,
ModuleExtensionList $moduleList,
ModuleInstallerInterface $moduleInstaller,
TwigEnvironment $twig,
UuidInterface $uuid,
string $sitePath,
?Filesystem $fs = NULL,
?Random $random = NULL
) {
$this->entityTypeManager = $entityTypeManager;
$this->spellManager = $spellManager;
$this->shellProcessFactory = $shellProcessFactory;
$this->moduleList = $moduleList;
$this->moduleInstaller = $moduleInstaller;
$this->twig = $twig;
$this->uuid = $uuid;
$this->sitePath = $sitePath;
$this->fs = $fs ?: new Filesystem();
$this->random = $random ?: new Random();
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$messenger,
$logger,
$stringTranslation,
$utils,
$configFactory,
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'machine_name' => '',
'migration_module' => 'app_dc',
'migration_group' => 'app_dummy',
];
}
protected function populateCalculatedConfigurationValues(): static {
parent::populateCalculatedConfigurationValues();
if (empty($this->configuration['machine_name'])) {
throw new \InvalidArgumentException('machine_name is required');
}
return $this;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$configuration = $this->getConfiguration();
$configEntityType = $this->getConfigEntityType();
$form['machine_name'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $configEntityType?->getLabel() ?: $this->t('Bundle'),
'#description' => $this->t(
'The machine-name of the @entity_type.label to create the migration for.',
[
'@entity_type.label' => $configEntityType?->getLabel() ?: $this->t('Config Entity'),
],
),
'#default_value' => $configuration['machine_name'],
'#autocomplete_route_name' => 'devel_wizard.autocomplete.config_entity_instance',
'#autocomplete_route_parameters' => [
'entityTypeId' => $this->configEntityTypeId,
],
];
$form['migration_module'] = [
'#title' => $this->t('Migration module'),
'#type' => 'textfield',
'#required' => TRUE,
'#default_value' => $configuration['migration_module'],
'#description' => $this->t('A custom module machine-name where migration definition YAML files will be placed, under {machine-name}/config/install folder.'),
'#autocomplete_route_name' => 'devel_wizard.autocomplete.module',
];
$form['migration_group'] = [
'#title' => $this->t('Migration group'),
'#type' => 'textfield',
'#required' => TRUE,
'#default_value' => $configuration['migration_group'],
'#description' => $this->t('A name for the migration group eg: app_dummy, app_default.'),
'#autocomplete_route_name' => 'devel_wizard.autocomplete.config_entity_instance',
'#autocomplete_route_parameters' => ['entityTypeId' => 'migration_group'],
];
return $form;
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// @todo Implement validateConfigurationForm() method.
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValue($form['#parents'], []);
$this->setConfiguration($values);
}
/**
* @throws \Twig\Error\Error
*/
protected function doIt(): static {
if (!isset($this->batchContext['sandbox']['current_step'])) {
$this->batchContext['sandbox']['current_step'] = 'composer_packages_require';
}
switch ($this->batchContext['sandbox']['current_step']) {
case 'composer_packages_require':
$this->doItComposerRequire();
drupal_flush_all_caches();
$this->batchContext['message'] = $this->t('Create migration related modules');
$this->batchContext['sandbox']['current_step'] = 'module_create';
$this->batchContext['sandbox']['finished'] = 0.4;
break;
case 'module_create':
$this->doItModuleCreate();
drupal_flush_all_caches();
$this->batchContext['message'] = $this->t('Enable migration related modules');
$this->batchContext['sandbox']['current_step'] = 'module_install';
$this->batchContext['sandbox']['finished'] = 0.5;
break;
case 'module_install':
$this->doItModuleInstall();
drupal_flush_all_caches();
$this->batchContext['message'] = $this->t('Create migration definitions and dummy data sources.');
$this->batchContext['sandbox']['current_step'] = 'migration_create';
$this->batchContext['sandbox']['finished'] = 0.8;
break;
case 'migration_create':
$this->doItMigrationCreate();
drupal_flush_all_caches();
$this->batchContext['sandbox']['current_step'] = '_finished';
$this->batchContext['sandbox']['finished'] = 1.0;
break;
case '_finished':
$this->batchContext['sandbox']['finished'] = 1.0;
break;
}
return $this;
}
protected function doItComposerRequire(): static {
$this->installComposerPackages('prod', $this->getRequiredPackagesProd());
$this->installComposerPackages('dev', $this->getRequiredPackagesDev());
return $this;
}
protected function doItModuleCreate(): static {
// @todo Implement this step as "sub_spell".
$configuration = $this->getConfiguration();
$machineName = $configuration['migration_module'];
if ($this->moduleList->exists($machineName)) {
return $this;
}
/* @noinspection PhpUnhandledExceptionInspection */
$moduleSpell = $this->spellManager->createInstance(
'devel_wizard_module',
[
'type' => 'custom',
'machine_name' => $configuration['migration_module'],
'name' => 'App - Default and Dummy migrations',
'description' => 'Provides content migrations',
'package' => 'App',
'info.yml' => [
'dependencies' => [
'migrate_dc:migrate_dc',
],
],
],
);
$subBatchContext = [];
$moduleSpell->prepare();
$moduleSpell->abracadabra($subBatchContext);
return $this;
}
protected function doItModuleInstall(): static {
$configuration = $this->getConfiguration();
$machineName = $configuration['migration_module'];
$this->ensureModuleInstalled('migrate_tools');
$this->ensureModuleInstalled('migrate_plus');
$this->ensureModuleInstalled('migrate_dc');
$this->ensureModuleInstalled($machineName);
return $this;
}
/**
* @throws \Twig\Error\Error
*/
protected function doItMigrationCreate(): static {
$this
->doItMigrationCreateGroup()
->doItMigrationCreateDefinitions()
->doItMigrationCreateSource();
return $this;
}
/**
* @throws \Twig\Error\Error
*/
protected function doItMigrationCreateGroup(): static {
$configuration = $this->getConfiguration();
$moduleName = $configuration['migration_module'];
$modulePath = $this->getModulePath($moduleName);
$migrationGroup = $configuration['migration_group'];
$filePath = Path::join(
$modulePath,
'config',
'install',
"migrate_plus.migration_group.{$migrationGroup}.yml",
);
$values = [
'migration_group' => $migrationGroup,
'migration_module' => $moduleName,
'site_path' => $this->sitePath,
];
$templatePath = '@devel_wizard/migration/devel_wizard.migration_group.yml.twig';
$rendered = $this->twig->render($templatePath, $values);
$this->fs->mkdir(Path::getDirectory($filePath));
$this->fs->dumpFile($filePath, $rendered);
$this->messageFilesystemEntryCreate($filePath);
return $this;
}
/**
* @throws \Twig\Error\Error
*/
protected function doItMigrationCreateDefinitions(): static {
$configuration = $this->getConfiguration();
$bundleId = $configuration['machine_name'];
$moduleName = $configuration['migration_module'];
$modulePath = $this->getModulePath($moduleName);
$migrationGroup = $configuration['migration_group'];
$migrationId = "{$migrationGroup}__{$this->contentEntityTypeId}__{$bundleId}";
// @todo Make it configurable "plugin" vs "config".
$filePath = Path::join(
$modulePath,
'config',
'install',
"migrate_plus.migration.{$migrationId}.yml",
);
$tplPath = "@devel_wizard/migration/devel_wizard.migration_definition.{$this->contentEntityTypeId}.yml.twig";
$tplValues = [
'entity_type' => $this->contentEntityTypeId,
'migration_id' => $migrationId,
'bundle' => $bundleId,
'migration_group' => $migrationGroup,
];
$this->fs->mkdir(Path::getDirectory($filePath));
$this->fs->dumpFile($filePath, $this->twig->render($tplPath, $tplValues));
$this->messageFilesystemEntryCreate($filePath);
return $this;
}
protected function doItMigrationCreateSource(): static {
$configuration = $this->getConfiguration();
$nodeTypeId = $configuration['machine_name'];
$migrationGroup = $configuration['migration_group'];
$filePath = Path::join(
'..',
$this->sitePath,
'content',
$migrationGroup,
$this->contentEntityTypeId,
"{$nodeTypeId}.yml",
);
$sourceData = $this->getMigrationSourceData();
$this->fs->mkdir(Path::getDirectory($filePath));
$this->fs->dumpFile($filePath, Yaml::encode($sourceData));
$this->messageFilesystemEntryCreate($filePath);
return $this;
}
abstract protected function getMigrationSourceData(): array;
public function getRequiredPackagesProd(): array {
return [];
}
public function getRequiredPackagesDev(): array {
return [
'drupal/migrate_tools' => '^6.0',
'drupal/migrate_plus' => '^6.0',
'drupal/migrate_dc' => '2.x-dev',
];
}
}
