annotate modules/contrib/migrate_tools/src/MigrateExecutable.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /**
Chris@0 4 * @file
Chris@0 5 * Contains \Drupal\migrate_tools\MigrateExecutable.
Chris@0 6 */
Chris@0 7
Chris@0 8 namespace Drupal\migrate_tools;
Chris@0 9
Chris@0 10 use Drupal\migrate\Event\MigratePreRowSaveEvent;
Chris@0 11 use Drupal\migrate\Event\MigrateRollbackEvent;
Chris@0 12 use Drupal\migrate\Event\MigrateRowDeleteEvent;
Chris@0 13 use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
Chris@0 14 use Drupal\migrate\MigrateMessageInterface;
Chris@0 15 use Drupal\migrate\Plugin\MigrationInterface;
Chris@0 16 use Drupal\migrate\MigrateSkipRowException;
Chris@0 17 use Drupal\migrate\Plugin\MigrateIdMapInterface;
Chris@0 18 use Drupal\migrate\Event\MigrateEvents;
Chris@0 19 use Drupal\migrate_plus\Event\MigrateEvents as MigratePlusEvents;
Chris@0 20 use Drupal\migrate\Event\MigrateMapSaveEvent;
Chris@0 21 use Drupal\migrate\Event\MigrateMapDeleteEvent;
Chris@0 22 use Drupal\migrate\Event\MigrateImportEvent;
Chris@0 23 use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
Chris@0 24
Chris@0 25 class MigrateExecutable extends MigrateExecutableBase {
Chris@0 26
Chris@0 27 /**
Chris@0 28 * Counters of map statuses.
Chris@0 29 *
Chris@0 30 * @var array
Chris@0 31 * Set of counters, keyed by MigrateIdMapInterface::STATUS_* constant.
Chris@0 32 */
Chris@0 33 protected $saveCounters = array(
Chris@0 34 MigrateIdMapInterface::STATUS_FAILED => 0,
Chris@0 35 MigrateIdMapInterface::STATUS_IGNORED => 0,
Chris@0 36 MigrateIdMapInterface::STATUS_IMPORTED => 0,
Chris@0 37 MigrateIdMapInterface::STATUS_NEEDS_UPDATE => 0,
Chris@0 38 );
Chris@0 39
Chris@0 40 /**
Chris@0 41 * Counter of map deletions.
Chris@0 42 *
Chris@0 43 * @var int
Chris@0 44 */
Chris@0 45 protected $deleteCounter = 0;
Chris@0 46
Chris@0 47 /**
Chris@0 48 * Maximum number of items to process in this migration. 0 indicates no limit
Chris@0 49 * is to be applied.
Chris@0 50 *
Chris@0 51 * @var int
Chris@0 52 */
Chris@0 53 protected $itemLimit = 0;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Frequency (in items) at which progress messages should be emitted.
Chris@0 57 *
Chris@0 58 * @var int
Chris@0 59 */
Chris@0 60 protected $feedback = 0;
Chris@0 61
Chris@0 62 /**
Chris@0 63 * List of specific source IDs to import.
Chris@0 64 *
Chris@0 65 * @var array
Chris@0 66 */
Chris@0 67 protected $idlist = [];
Chris@0 68
Chris@0 69 /**
Chris@0 70 * Count of number of items processed so far in this migration.
Chris@0 71 * @var int
Chris@0 72 */
Chris@0 73 protected $counter = 0;
Chris@0 74
Chris@0 75 /**
Chris@0 76 * Whether the destination item exists before saving.
Chris@0 77 *
Chris@0 78 * @var bool
Chris@0 79 */
Chris@0 80 protected $preExistingItem = FALSE;
Chris@0 81
Chris@0 82 /**
Chris@0 83 * List of event listeners we have registered.
Chris@0 84 *
Chris@0 85 * @var array
Chris@0 86 */
Chris@0 87 protected $listeners = [];
Chris@0 88
Chris@0 89 /**
Chris@0 90 * {@inheritdoc}
Chris@0 91 */
Chris@0 92 public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, array $options = []) {
Chris@0 93 parent::__construct($migration, $message);
Chris@0 94 if (isset($options['limit'])) {
Chris@0 95 $this->itemLimit = $options['limit'];
Chris@0 96 }
Chris@0 97 if (isset($options['feedback'])) {
Chris@0 98 $this->feedback = $options['feedback'];
Chris@0 99 }
Chris@0 100 if (isset($options['idlist'])) {
Chris@0 101 $this->idlist = explode(',', $options['idlist']);
Chris@0 102 }
Chris@0 103
Chris@0 104 $this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave'];
Chris@0 105 $this->listeners[MigrateEvents::MAP_DELETE] = [$this, 'onMapDelete'];
Chris@0 106 $this->listeners[MigrateEvents::POST_IMPORT] = [$this, 'onPostImport'];
Chris@0 107 $this->listeners[MigrateEvents::POST_ROLLBACK] = [$this, 'onPostRollback'];
Chris@0 108 $this->listeners[MigrateEvents::PRE_ROW_SAVE] = [$this, 'onPreRowSave'];
Chris@0 109 $this->listeners[MigrateEvents::POST_ROW_DELETE] = [$this, 'onPostRowDelete'];
Chris@0 110 $this->listeners[MigratePlusEvents::PREPARE_ROW] = [$this, 'onPrepareRow'];
Chris@0 111 foreach ($this->listeners as $event => $listener) {
Chris@0 112 \Drupal::service('event_dispatcher')->addListener($event, $listener);
Chris@0 113 }
Chris@0 114 }
Chris@0 115
Chris@0 116 /**
Chris@0 117 * Count up any map save events.
Chris@0 118 *
Chris@0 119 * @param \Drupal\migrate\Event\MigrateMapSaveEvent $event
Chris@0 120 * The map event.
Chris@0 121 */
Chris@0 122 public function onMapSave(MigrateMapSaveEvent $event) {
Chris@0 123 // Only count saves for this migration.
Chris@0 124 if ($event->getMap()->getQualifiedMapTableName() == $this->migration->getIdMap()->getQualifiedMapTableName()) {
Chris@0 125 $fields = $event->getFields();
Chris@0 126 // Distinguish between creation and update.
Chris@0 127 if ($fields['source_row_status'] == MigrateIdMapInterface::STATUS_IMPORTED &&
Chris@0 128 $this->preExistingItem
Chris@0 129 ) {
Chris@0 130 $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE]++;
Chris@0 131 }
Chris@0 132 else {
Chris@0 133 $this->saveCounters[$fields['source_row_status']]++;
Chris@0 134 }
Chris@0 135 }
Chris@0 136 }
Chris@0 137
Chris@0 138 /**
Chris@0 139 * Count up any rollback events.
Chris@0 140 *
Chris@0 141 * @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event
Chris@0 142 * The map event.
Chris@0 143 */
Chris@0 144 public function onMapDelete(MigrateMapDeleteEvent $event) {
Chris@0 145 $this->deleteCounter++;
Chris@0 146 }
Chris@0 147
Chris@0 148 /**
Chris@0 149 * Return the number of items created.
Chris@0 150 *
Chris@0 151 * @return int
Chris@0 152 */
Chris@0 153 public function getCreatedCount() {
Chris@0 154 return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED];
Chris@0 155 }
Chris@0 156
Chris@0 157 /**
Chris@0 158 * Return the number of items updated.
Chris@0 159 *
Chris@0 160 * @return int
Chris@0 161 */
Chris@0 162 public function getUpdatedCount() {
Chris@0 163 return $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE];
Chris@0 164 }
Chris@0 165
Chris@0 166 /**
Chris@0 167 * Return the number of items ignored.
Chris@0 168 *
Chris@0 169 * @return int
Chris@0 170 */
Chris@0 171 public function getIgnoredCount() {
Chris@0 172 return $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED];
Chris@0 173 }
Chris@0 174
Chris@0 175 /**
Chris@0 176 * Return the number of items that failed.
Chris@0 177 *
Chris@0 178 * @return int
Chris@0 179 */
Chris@0 180 public function getFailedCount() {
Chris@0 181 return $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
Chris@0 182 }
Chris@0 183
Chris@0 184 /**
Chris@0 185 * Return the total number of items processed. Note that STATUS_NEEDS_UPDATE
Chris@0 186 * is not counted, since this is typically set on stubs created as side
Chris@0 187 * effects, not on the primary item being imported.
Chris@0 188 *
Chris@0 189 * @return int
Chris@0 190 */
Chris@0 191 public function getProcessedCount() {
Chris@0 192 return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED] +
Chris@0 193 $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE] +
Chris@0 194 $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED] +
Chris@0 195 $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
Chris@0 196 }
Chris@0 197
Chris@0 198 /**
Chris@0 199 * Return the number of items rolled back.
Chris@0 200 *
Chris@0 201 * @return int
Chris@0 202 */
Chris@0 203 public function getRollbackCount() {
Chris@0 204 return $this->deleteCounter;
Chris@0 205 }
Chris@0 206
Chris@0 207 /**
Chris@0 208 * Reset all the per-status counters to 0.
Chris@0 209 */
Chris@0 210 protected function resetCounters() {
Chris@0 211 foreach ($this->saveCounters as $status => $count) {
Chris@0 212 $this->saveCounters[$status] = 0;
Chris@0 213 }
Chris@0 214 $this->deleteCounter = 0;
Chris@0 215 }
Chris@0 216
Chris@0 217 /**
Chris@0 218 * React to migration completion.
Chris@0 219 *
Chris@0 220 * @param \Drupal\migrate\Event\MigrateImportEvent $event
Chris@0 221 * The map event.
Chris@0 222 */
Chris@0 223 public function onPostImport(MigrateImportEvent $event) {
Chris@0 224 $migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
Chris@0 225 $migrate_last_imported_store->set($event->getMigration()->id(), round(microtime(TRUE) * 1000));
Chris@0 226 $this->progressMessage();
Chris@0 227 $this->removeListeners();
Chris@0 228 }
Chris@0 229
Chris@0 230 /**
Chris@0 231 * Clean up all our event listeners.
Chris@0 232 */
Chris@0 233 protected function removeListeners() {
Chris@0 234 foreach ($this->listeners as $event => $listener) {
Chris@0 235 \Drupal::service('event_dispatcher')->removeListener($event, $listener);
Chris@0 236 }
Chris@0 237 }
Chris@0 238
Chris@0 239 /**
Chris@0 240 * Emit information on what we've done since the last feedback (or the
Chris@0 241 * beginning of this migration).
Chris@0 242 *
Chris@0 243 * @param bool $done
Chris@0 244 */
Chris@0 245 protected function progressMessage($done = TRUE) {
Chris@0 246 $processed = $this->getProcessedCount();
Chris@0 247 if ($done) {
Chris@0 248 $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
Chris@0 249 $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
Chris@0 250 }
Chris@0 251 else {
Chris@0 252 $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
Chris@0 253 $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
Chris@0 254 }
Chris@0 255 $this->message->display(\Drupal::translation()->formatPlural($processed,
Chris@0 256 $singular_message, $plural_message,
Chris@0 257 array('@numitems' => $processed,
Chris@0 258 '@created' => $this->getCreatedCount(),
Chris@0 259 '@updated' => $this->getUpdatedCount(),
Chris@0 260 '@failures' => $this->getFailedCount(),
Chris@0 261 '@ignored' => $this->getIgnoredCount(),
Chris@0 262 '@name' => $this->migration->id())));
Chris@0 263 }
Chris@0 264
Chris@0 265 /**
Chris@0 266 * React to rollback completion.
Chris@0 267 *
Chris@0 268 * @param \Drupal\migrate\Event\MigrateRollbackEvent $event
Chris@0 269 * The map event.
Chris@0 270 */
Chris@0 271 public function onPostRollback(MigrateRollbackEvent $event) {
Chris@0 272 $this->rollbackMessage();
Chris@0 273 $this->removeListeners();
Chris@0 274 }
Chris@0 275
Chris@0 276 /**
Chris@0 277 * Emit information on what we've done since the last feedback (or the
Chris@0 278 * beginning of this migration).
Chris@0 279 *
Chris@0 280 * @param bool $done
Chris@0 281 */
Chris@0 282 protected function rollbackMessage($done = TRUE) {
Chris@0 283 $rolled_back = $this->getRollbackCount();
Chris@0 284 if ($done) {
Chris@0 285 $singular_message = "Rolled back 1 item - done with '@name'";
Chris@0 286 $plural_message = "Rolled back @numitems items - done with '@name'";
Chris@0 287 }
Chris@0 288 else {
Chris@0 289 $singular_message = "Rolled back 1 item - continuing with '@name'";
Chris@0 290 $plural_message = "Rolled back @numitems items - continuing with '@name'";
Chris@0 291 }
Chris@0 292 $this->message->display(\Drupal::translation()->formatPlural($rolled_back,
Chris@0 293 $singular_message, $plural_message,
Chris@0 294 array('@numitems' => $rolled_back,
Chris@0 295 '@name' => $this->migration->id())));
Chris@0 296 }
Chris@0 297
Chris@0 298 /**
Chris@0 299 * React to an item about to be imported.
Chris@0 300 *
Chris@0 301 * @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
Chris@0 302 * The pre-save event.
Chris@0 303 */
Chris@0 304 public function onPreRowSave(MigratePreRowSaveEvent $event) {
Chris@0 305 $id_map = $event->getRow()->getIdMap();
Chris@0 306 if (!empty($id_map['destid1'])) {
Chris@0 307 $this->preExistingItem = TRUE;
Chris@0 308 }
Chris@0 309 else {
Chris@0 310 $this->preExistingItem = FALSE;
Chris@0 311 }
Chris@0 312 }
Chris@0 313
Chris@0 314 /**
Chris@0 315 * React to item rollback.
Chris@0 316 *
Chris@0 317 * @param \Drupal\migrate\Event\MigrateRowDeleteEvent $event
Chris@0 318 * The post-save event.
Chris@0 319 */
Chris@0 320 public function onPostRowDelete(MigrateRowDeleteEvent $event) {
Chris@0 321 if ($this->feedback && ($this->deleteCounter) && $this->deleteCounter % $this->feedback == 0) {
Chris@0 322 $this->rollbackMessage(FALSE);
Chris@0 323 $this->resetCounters();
Chris@0 324 }
Chris@0 325 }
Chris@0 326
Chris@0 327 /**
Chris@0 328 * React to a new row.
Chris@0 329 *
Chris@0 330 * @param \Drupal\migrate_plus\Event\MigratePrepareRowEvent $event
Chris@0 331 * The prepare-row event.
Chris@0 332 *
Chris@0 333 * @throws \Drupal\migrate\MigrateSkipRowException
Chris@0 334 *
Chris@0 335 */
Chris@0 336 public function onPrepareRow(MigratePrepareRowEvent $event) {
Chris@0 337 if ($this->idlist) {
Chris@0 338 $row = $event->getRow();
Chris@0 339 $source_id = $row->getSourceIdValues();
Chris@0 340 if (!in_array(reset($source_id), $this->idlist)) {
Chris@0 341 throw new MigrateSkipRowException(NULL, FALSE);
Chris@0 342 }
Chris@0 343 }
Chris@0 344 if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
Chris@0 345 $this->progressMessage(FALSE);
Chris@0 346 $this->resetCounters();
Chris@0 347 }
Chris@0 348 $this->counter++;
Chris@0 349 if ($this->itemLimit && $this->counter >= $this->itemLimit) {
Chris@0 350 $event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
Chris@0 351 }
Chris@0 352
Chris@0 353 }
Chris@0 354
Chris@0 355 }