Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / migrate_tools / src / MigrateBatchExecutable.php
1 <?php
2
3 namespace Drupal\migrate_tools;
4
5 use Drupal\migrate\MigrateMessage;
6 use Drupal\migrate\MigrateMessageInterface;
7 use Drupal\migrate\Plugin\Migration;
8 use Drupal\migrate\Plugin\MigrationInterface;
9
10 /**
11  * Defines a migrate executable class for batch migrations through UI.
12  */
13 class MigrateBatchExecutable extends MigrateExecutable {
14
15   /**
16    * Representing a batch import operation.
17    */
18   const BATCH_IMPORT = 1;
19
20   /**
21    * Indicates if we need to update existing rows or skip them.
22    *
23    * @var int
24    */
25   protected $updateExistingRows = 0;
26
27   /**
28    * Indicates if we need import dependent migrations also.
29    *
30    * @var int
31    */
32   protected $checkDependencies = 0;
33
34   /**
35    * The current batch context.
36    *
37    * @var array
38    */
39   protected $batchContext = [];
40
41   /**
42    * Plugin manager for migration plugins.
43    *
44    * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
45    */
46   protected $migrationPluginManager;
47
48   /**
49    * {@inheritdoc}
50    */
51   public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, array $options = []) {
52
53     if (isset($options['update'])) {
54       $this->updateExistingRows = $options['update'];
55     }
56
57     if (isset($options['force'])) {
58       $this->checkDependencies = $options['force'];
59     }
60
61     parent::__construct($migration, $message, $options);
62     $this->migrationPluginManager = \Drupal::getContainer()->get('plugin.manager.migration');
63   }
64
65   /**
66    * Sets the current batch content so listeners can update the messages.
67    *
68    * @param array $context
69    *   The batch context.
70    */
71   public function setBatchContext(array &$context) {
72     $this->batchContext = &$context;
73   }
74
75   /**
76    * Gets a reference to the current batch context.
77    *
78    * @return array
79    *   The batch context.
80    */
81   public function &getBatchContext() {
82     return $this->batchContext;
83   }
84
85   /**
86    * Setup batch operations for running the migration.
87    */
88   public function batchImport() {
89     // Create the batch operations for each migration that needs to be executed.
90     // This includes the migration for this executable, but also the dependent
91     // migrations.
92     $operations = $this->batchOperations([$this->migration], 'import', [
93       'limit' => $this->itemLimit,
94       'update' => $this->updateExistingRows,
95       'force' => $this->checkDependencies,
96     ]);
97
98     if (count($operations) > 0) {
99       $batch = [
100         'operations' => $operations,
101         'title' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
102         'init_message' => t('Start migrating %migrate', ['%migrate' => $this->migration->label()]),
103         'progress_message' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
104         'error_message' => t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
105         'finished' => '\Drupal\migrate_tools\MigrateBatchExecutable::batchFinishedImport',
106       ];
107
108       batch_set($batch);
109     }
110   }
111
112   /**
113    * Helper to generate the batch operations for importing migrations.
114    *
115    * @param \Drupal\migrate\Plugin\MigrationInterface[] $migrations
116    *   The migrations.
117    * @param string $operation
118    *   The batch operation to perform.
119    * @param array $options
120    *   The migration options.
121    *
122    * @return array
123    *   The batch operations to perform.
124    */
125   protected function batchOperations(array $migrations, $operation, array $options = []) {
126     $operations = [];
127     foreach ($migrations as $id => $migration) {
128
129       if (!empty($options['update'])) {
130         $migration->getIdMap()->prepareUpdate();
131       }
132
133       if (!empty($options['force'])) {
134         $migration->set('requirements', []);
135       }
136       else {
137         $dependencies = $migration->getMigrationDependencies();
138         if (!empty($dependencies['required'])) {
139           $required_migrations = $this->migrationPluginManager->createInstances($dependencies['required']);
140           // For dependent migrations will need to be migrate all items.
141           $dependent_options = $options;
142           $dependent_options['limit'] = 0;
143           $operations += $this->batchOperations($required_migrations, $operation, [
144             'limit' => 0,
145             'update' => $options['update'],
146             'force' => $options['force'],
147           ]);
148         }
149       }
150
151       $operations[] = [
152         '\Drupal\migrate_tools\MigrateBatchExecutable::batchProcessImport',
153         [$migration->id(), $options],
154       ];
155     }
156
157     return $operations;
158   }
159
160   /**
161    * Batch 'operation' callback.
162    *
163    * @param string $migration_id
164    *   The migration id.
165    * @param array $options
166    *   The batch executable options.
167    * @param array $context
168    *   The sandbox context.
169    */
170   public static function batchProcessImport($migration_id, array $options, array &$context) {
171     if (empty($context['sandbox'])) {
172       $context['finished'] = 0;
173       $context['sandbox'] = [];
174       $context['sandbox']['total'] = 0;
175       $context['sandbox']['counter'] = 0;
176       $context['sandbox']['batch_limit'] = 0;
177       $context['sandbox']['operation'] = MigrateBatchExecutable::BATCH_IMPORT;
178     }
179
180     // Prepare the migration executable.
181     $message = new MigrateMessage();
182     /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
183     $migration = \Drupal::getContainer()->get('plugin.manager.migration')->createInstance($migration_id);
184     $executable = new MigrateBatchExecutable($migration, $message, $options);
185
186     if (empty($context['sandbox']['total'])) {
187       $context['sandbox']['total'] = $executable->getSource()->count();
188       $context['sandbox']['batch_limit'] = $executable->calculateBatchLimit($context);
189       $context['results'][$migration->id()] = [
190         '@numitems' => 0,
191         '@created' => 0,
192         '@updated' => 0,
193         '@failures' => 0,
194         '@ignored' => 0,
195         '@name' => $migration->id(),
196       ];
197     }
198
199     // Every iteration, we reset out batch counter.
200     $context['sandbox']['batch_counter'] = 0;
201
202     // Make sure we know our batch context.
203     $executable->setBatchContext($context);
204
205     // Do the import.
206     $result = $executable->import();
207
208     // Store the result; will need to combine the results of all our iterations.
209     $context['results'][$migration->id()] = [
210       '@numitems' => $context['results'][$migration->id()]['@numitems'] + $executable->getProcessedCount(),
211       '@created' => $context['results'][$migration->id()]['@created'] + $executable->getCreatedCount(),
212       '@updated' => $context['results'][$migration->id()]['@updated'] + $executable->getUpdatedCount(),
213       '@failures' => $context['results'][$migration->id()]['@failures'] + $executable->getFailedCount(),
214       '@ignored' => $context['results'][$migration->id()]['@ignored'] + $executable->getIgnoredCount(),
215       '@name' => $migration->id(),
216     ];
217
218     // Do some housekeeping.
219     if (
220       $result != MigrationInterface::RESULT_INCOMPLETE
221     ) {
222       $context['finished'] = 1;
223     }
224     else {
225       $context['sandbox']['counter'] = $context['results'][$migration->id()]['@numitems'];
226       if ($context['sandbox']['counter'] <= $context['sandbox']['total']) {
227         $context['finished'] = ((float) $context['sandbox']['counter'] / (float) $context['sandbox']['total']);
228         $context['message'] = t('Importing %migration (@percent%).', [
229           '%migration' => $migration->label(),
230           '@percent' => (int) ($context['finished'] * 100),
231         ]);
232       }
233     }
234
235   }
236
237   /**
238    * Finished callback for import batches.
239    *
240    * @param bool $success
241    *   A boolean indicating whether the batch has completed successfully.
242    * @param array $results
243    *   The value set in $context['results'] by callback_batch_operation().
244    * @param array $operations
245    *   If $success is FALSE, contains the operations that remained unprocessed.
246    */
247   public static function batchFinishedImport($success, array $results, array $operations) {
248     if ($success) {
249       foreach ($results as $migration_id => $result) {
250         $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
251         $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
252         drupal_set_message(\Drupal::translation()->formatPlural($result['@numitems'],
253           $singular_message,
254           $plural_message,
255           $result));
256       }
257     }
258   }
259
260   /**
261    * {@inheritdoc}
262    */
263   public function checkStatus() {
264     $status = parent::checkStatus();
265
266     if ($status == MigrationInterface::RESULT_COMPLETED) {
267       // Do some batch housekeeping.
268       $context = $this->getBatchContext();
269
270       if (!empty($context['sandbox']) && $context['sandbox']['operation'] == MigrateBatchExecutable::BATCH_IMPORT) {
271         $context['sandbox']['batch_counter']++;
272         if ($context['sandbox']['batch_counter'] >= $context['sandbox']['batch_limit']) {
273           $status = MigrationInterface::RESULT_INCOMPLETE;
274         }
275       }
276     }
277
278     return $status;
279   }
280
281   /**
282    * Calculates how much a single batch iteration will handle.
283    *
284    * @param array $context
285    *   The sandbox context.
286    *
287    * @return float
288    *   The batch limit.
289    */
290   public function calculateBatchLimit(array $context) {
291     // TODO Maybe we need some other more sophisticated logic here?
292     return ceil($context['sandbox']['total'] / 100);
293   }
294
295 }