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 }
|