migmag-1.0.x-dev/migmag_rollbackable/src/Traits/RollbackableTargetTrait.php
migmag_rollbackable/src/Traits/RollbackableTargetTrait.php
<?php
declare(strict_types=1);
namespace Drupal\migmag_rollbackable\Traits;
use Drupal\Core\Config\Config;
use Drupal\migrate\Plugin\migrate\destination\Config as DefaultConfigDestination;
use Drupal\migrate\Row;
/**
* Target related common operations for rollbackable migration destinations.
*/
trait RollbackableTargetTrait {
/**
* Determines the target ID from the given destination IDs or migration row.
*
* @param \Drupal\migrate\Row|array $row_or_destination_ids
* A migration row or an array of the destination IDs.
*
* @return string
* The ID of the target (object).
*/
protected function getTargetObjectId($row_or_destination_ids): string {
if ($row_or_destination_ids instanceof Row) {
$destination_ids = [];
foreach (array_keys($this->getIds()) as $id) {
$destination_ids[] = $row_or_destination_ids->getDestinationProperty($id);
}
}
return implode('.', array_slice($destination_ids ?? $row_or_destination_ids, 0, 3));
}
/**
* Determines the component from the given destination IDs or migration row.
*
* @param \Drupal\migrate\Row|array $row_or_destination_ids
* A migration row or an array of the destination IDs.
*
* @return string
* The component of the target (object).
*/
protected function getTargetComponent($row_or_destination_ids): string {
return '';
}
/**
* Determines the language code of the actual target.
*
* @param \Drupal\migrate\Row|array $row_or_destination_ids
* A migration row or an array of the destination IDs.
*
* @return string
* The language code. Optional, defaults to ''.
*/
protected function getTargetLangcode($row_or_destination_ids): string {
return '';
}
/**
* Returns the target object of the rollbackable destination plugin.
*
* @param string $target_object_id
* The ID of the target object (a configuration, entity etc).
* @param string $langcode
* The language code.
*
* @return object|null
* The target object (a config entity or module settings). NULL is allowed
* because not every destination plugin operates on objects.
*
* @see \Drupal\shortcut\Plugin\migrate\destination\ShortcutSetUsers
*/
protected function getTargetObject(string $target_object_id, string $langcode) {
return $this->configFactory->getEditable($target_object_id);
}
/**
* Returns the destination IDs to save.
*
* @param true|array $parent_destination_ids
* The destination IDs returned by the original destination plugin.
* @param string $target_object_id
* The ID of the target object.
* @param string $component
* The ID of the target component. Might be an empty string.
* @param string $langcode
* The language code of the target object. Might be an empty string.
*
* @return array
* The destination IDs to save.
*/
protected function generateDestinationIds($parent_destination_ids, string $target_object_id, string $component, string $langcode): array {
return $parent_destination_ids;
}
/**
* Deletes the target object of the rollbackable destination plugin.
*
* @param string $target_object_id
* The ID of the target object (a configuration, entity etc).
* @param string $langcode
* The language code.
*/
protected function deleteTargetObject(string $target_object_id, string $langcode): void {
$this->getTargetObject($target_object_id, $langcode)->delete();
}
/**
* Determines whether the target is new.
*
* @param string $target_object_id
* The ID of the target object (a configuration, entity etc).
* @param string $langcode
* The language code.
*
* @return bool
* TRUE when the target is evaluated as being new, FALSE otherwise.
*/
protected function targetObjectIsNew(string $target_object_id, string $langcode): bool {
return $this->getTargetObject($target_object_id, $langcode)->isNew();
}
/**
* Determines whether the target component is new.
*
* @param string $target_object_id
* The ID of the target object (a configuration, entity etc).
* @param string $component
* The name (an internal unique identifier) of the component.
* @param string $langcode
* The language code.
*
* @return bool
* TRUE when the target component is evaluated as being new, FALSE
* otherwise.
*/
protected function targetObjectComponentIsNew(string $target_object_id, string $component, string $langcode): bool {
$target_object = $this->getTargetObject($target_object_id, $langcode);
return !array_key_exists($component, $target_object->get());
}
/**
* Returns the previous value of the target before the actual migration.
*
* @param \Drupal\migrate\Row $row
* The processed migration row.
* @param string $target_object_id
* The ID of the target (object).
* @param string $component
* The component.
* @param string $langcode
* The language code.
*
* @return mixed
* The previous value of the target before the actual migration.
*/
protected function getPreviousValues(Row $row, string $target_object_id, string $component, string $langcode) {
if ($this->targetObjectIsNew($target_object_id, $langcode)) {
return NULL;
}
$target_object = $this->getTargetObject($target_object_id, $langcode);
// If we have a component, we have to return only the component's
// previous value.
if ($component) {
return $target_object instanceof Config
? $target_object->getOriginal($component, FALSE)
: $target_object->get($component);
}
// If the current configuration is a pre-existing config, we only have to
// restore the original values if the migration is rolled back. However, if
// the config was created by a migration, then we have to delete it if we
// cannot find any other rollbackable data that was stored by other
// migrations.
$rollback_data = [];
foreach ($row->getRawDestination() as $key => $value) {
// Destination which are instances of "config" have a special
// configuration for storing null values.
if (
$this instanceof DefaultConfigDestination &&
is_null($value) &&
empty($this->configuration['store null'])
) {
continue;
}
if ($mapped_key = $this->getMappedKey($key)) {
$top_level_key = explode(Row::PROPERTY_SEPARATOR, $this->getMappedKey($key))[0];
$rollback_data[$top_level_key] = $target_object instanceof Config
? $target_object->getOriginal($top_level_key, FALSE)
: $target_object->get($top_level_key);
}
}
return $rollback_data;
}
/**
* Returns a key of the target object based on the migration destination key.
*
* @param string $destination_key
* The destination property of the actual migration.
*
* @return string
* The corresponding configuration key of the target object.
*/
protected function getMappedKey(string $destination_key): ?string {
return $destination_key;
}
/**
* Restores the target's previous status and persists the change.
*
* @param mixed $rollback_data
* The data of the given target (or target component) before this migration
* was executed.
* @param string $target_object_id
* The ID of the target (object).
* @param string $component
* The component.
* @param string $langcode
* The language code.
*/
protected function doDataRollback($rollback_data, string $target_object_id, string $component, string $langcode): void {
// If there are previous values (aka "rollback data"), restore those. If
// there aren't, the component may be removed.
$target_object = $this->getTargetObject($target_object_id, $langcode);
if ($component) {
$rollback_data = [
$component => $rollback_data,
];
}
if (is_array($rollback_data)) {
foreach ($rollback_data as $config_key => $original_value) {
if ($original_value === NULL) {
$target_object->clear($config_key);
continue;
}
$target_object->set($config_key, $original_value);
}
$target_object->save();
}
}
}
