IncrementalCommand::_outputFileArray()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace N98\Magento\Command\System\Setup;
4
5
use Exception;
6
use N98\Magento\Command\AbstractMagentoCommand;
7
use ReflectionClass;
8
use RuntimeException;
9
use Symfony\Component\Console\Helper\DialogHelper;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Input\InputOption;
12
use Symfony\Component\Console\Output\OutputInterface;
13
14
/**
15
 * Class IncrementalCommand
16
 *
17
 * @package N98\Magento\Command\System\Setup
18
 * @codeCoverageIgnore
19
 */
20
class IncrementalCommand extends AbstractMagentoCommand
21
{
22
    const TYPE_MIGRATION_STRUCTURE = 'structure';
23
    const TYPE_MIGRATION_DATA = 'data';
24
25
    /**
26
     * @var OutputInterface
27
     */
28
    protected $_output;
29
30
    /**
31
     * @var InputInterface
32
     */
33
    protected $_input;
34
35
    /**
36
     * Holds our copy of the global config.
37
     *
38
     * Loaded to avoid grabbing the cached version, and so
39
     * we still have all our original information when we
40
     * destroy the real configuration
41
     *
42
     * @var mixed $_secondConfig
43
     */
44
    protected $_secondConfig;
45
46
    protected $_eventStash;
47
48
    /**
49
     * @var array
50
     */
51
    protected $_config;
52
53
    protected function configure()
54
    {
55
        $this
56
            ->setName('sys:setup:incremental')
57
            ->setDescription('List new setup scripts to run, then runs one script')
58
            ->addOption('stop-on-error', null, InputOption::VALUE_NONE, 'Stops execution of script on error')
59
            ->setHelp(
60
                'Examines an un-cached configuration tree and determines which ' .
61
                'structure and data setup resource scripts need to run, and then runs them.'
62
            );
63
    }
64
65
    /**
66
     * @param InputInterface $input
67
     * @param OutputInterface $output
68
     *
69
     * @return int|null|void
70
     */
71
    protected function execute(InputInterface $input, OutputInterface $output)
72
    {
73
        $this->_config = $this->getCommandConfig();
74
75
        //sets output so we can access it from all methods
76
        $this->_setOutput($output);
77
        $this->_setInput($input);
78
        if (false === $this->_init()) {
79
            return;
80
        }
81
        $needsUpdate = $this->_analyzeSetupResourceClasses();
82
83
        if (count($needsUpdate) === 0) {
84
            return;
85
        }
86
87
        $this->_listDetailedUpdateInformation($needsUpdate);
88
        $this->_runAllStructureUpdates($needsUpdate);
89
        $output->writeln('We have run all the setup resource scripts.');
90
    }
91
92
    protected function _loadSecondConfig()
93
    {
94
        $config = new \Mage_Core_Model_Config;
95
        $config->loadBase(); //get app/etc
96
        $this->_secondConfig = \Mage::getConfig()->loadModulesConfiguration('config.xml', $config);
97
    }
98
99
    /**
100
     * @return array
101
     */
102
    protected function _getAllSetupResourceObjects()
103
    {
104
        $config = $this->_secondConfig;
105
        $resources = $config->getNode('global/resources')->children();
106
        $setupResources = array();
107
        foreach ($resources as $name => $resource) {
108
            if (!$resource->setup) {
109
                continue;
110
            }
111
            $className = 'Mage_Core_Model_Resource_Setup';
112
            if (isset($resource->setup->class)) {
113
                $className = $resource->setup->getClassName();
114
            }
115
116
            $setupResources[$name] = new $className($name);
117
        }
118
119
        return $setupResources;
120
    }
121
122
    /**
123
     * @return \Mage_Core_Model_Resource
124
     */
125
    protected function _getResource()
126
    {
127
        return \Mage::getResourceSingleton('core/resource');
128
    }
129
130
    /**
131
     * @param \Mage_Core_Model_Resource_Setup $setupResource
132
     * @param array $args
133
     *
134
     * @return array|mixed
135
     */
136 View Code Duplication
    protected function _getAvaiableDbFilesFromResource($setupResource, $args = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
    {
138
        $result = $this->_callProtectedMethodFromObject('_getAvailableDbFiles', $setupResource, $args);
139
140
        //an install runs the install script first, then any upgrades
141
        if ($args[0] == \Mage_Core_Model_Resource_Setup::TYPE_DB_INSTALL) {
142
            $args[0] = \Mage_Core_Model_Resource_Setup::TYPE_DB_UPGRADE;
143
            $args[1] = $result[0]['toVersion'];
144
            $result = array_merge(
145
                $result,
146
                $this->_callProtectedMethodFromObject('_getAvailableDbFiles', $setupResource, $args)
147
            );
148
        }
149
150
        return $result;
151
    }
152
153
    /**
154
     * @param \Mage_Core_Model_Resource_Setup $setupResource
155
     * @param array $args
156
     *
157
     * @return array|mixed
158
     */
159 View Code Duplication
    protected function _getAvaiableDataFilesFromResource($setupResource, $args = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
    {
161
        $result = $this->_callProtectedMethodFromObject('_getAvailableDataFiles', $setupResource, $args);
162
        if ($args[0] == \Mage_Core_Model_Resource_Setup::TYPE_DATA_INSTALL) {
163
            $args[0] = \Mage_Core_Model_Resource_Setup::TYPE_DATA_UPGRADE;
164
            $args[1] = $result[0]['toVersion'];
165
            $result = array_merge(
166
                $result,
167
                $this->_callProtectedMethodFromObject('_getAvailableDbFiles', $setupResource, $args)
168
            );
169
        }
170
171
        return $result;
172
    }
173
174
    /**
175
     * @param string $method
176
     * @param object $object
177
     * @param array $args
178
     *
179
     * @return mixed
180
     */
181
    protected function _callProtectedMethodFromObject($method, $object, $args = array())
182
    {
183
        $r = new ReflectionClass($object);
184
        $m = $r->getMethod($method);
185
        $m->setAccessible(true);
186
187
        return $m->invokeArgs($object, $args);
188
    }
189
190
    /**
191
     * @param string $property
192
     * @param object $object
193
     * @param mixed $value
194
     */
195 View Code Duplication
    protected function _setProtectedPropertyFromObjectToValue($property, $object, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
    {
197
        $r = new ReflectionClass($object);
198
        $p = $r->getProperty($property);
199
        $p->setAccessible(true);
200
        $p->setValue($object, $value);
201
    }
202
203
    /**
204
     * @param string $property
205
     * @param object $object
206
     *
207
     * @return mixed
208
     */
209 View Code Duplication
    protected function _getProtectedPropertyFromObject($property, $object)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
    {
211
        $r = new ReflectionClass($object);
212
        $p = $r->getProperty($property);
213
        $p->setAccessible(true);
214
215
        return $p->getValue($object);
216
    }
217
218
    /**
219
     * @param string $name
220
     *
221
     * @return string
222
     */
223
    protected function _getDbVersionFromName($name)
224
    {
225
        return $this->_getResource()->getDbVersion($name);
226
    }
227
228
    /**
229
     * @param string $name
230
     *
231
     * @return string
232
     */
233
    protected function _getDbDataVersionFromName($name)
234
    {
235
        return $this->_getResource()->getDataVersion($name);
236
    }
237
238
    /**
239
     * @param Object $object
240
     *
241
     * @return mixed
242
     */
243
    protected function _getConfiguredVersionFromResourceObject($object)
244
    {
245
        $moduleConfig = $this->_getProtectedPropertyFromObject('_moduleConfig', $object);
246
247
        return $moduleConfig->version;
248
    }
249
250
    /**
251
     * @param bool|array $setupResources
252
     *
253
     * @return array
254
     */
255
    protected function _getAllSetupResourceObjectThatNeedUpdates($setupResources = false)
256
    {
257
        $setupResources = $setupResources ? $setupResources : $this->_getAllSetupResourceObjects();
258
        $needsUpdate = array();
259
        foreach ($setupResources as $name => $setupResource) {
0 ignored issues
show
Bug introduced by
The expression $setupResources of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
260
            $db_ver = $this->_getDbVersionFromName($name);
261
            $db_data_ver = $this->_getDbDataVersionFromName($name);
262
            $config_ver = $this->_getConfiguredVersionFromResourceObject($setupResource);
263
264
            if (
265
                (string) $config_ver == (string) $db_ver && //structure
266
                (string) $config_ver == (string) $db_data_ver //data
267
            ) {
268
                continue;
269
            }
270
            $needsUpdate[$name] = $setupResource;
271
        }
272
273
        return $needsUpdate;
274
    }
275
276
    /**
277
     * @param string $message
278
     */
279
    protected function _log($message)
280
    {
281
        $this->_output->writeln($message);
282
    }
283
284
    /**
285
     * @param OutputInterface $output
286
     */
287
    protected function _setOutput(OutputInterface $output)
288
    {
289
        $this->_output = $output;
290
    }
291
292
    /**
293
     * @param InputInterface $input
294
     */
295
    protected function _setInput(InputInterface $input)
296
    {
297
        $this->_input = $input;
298
    }
299
300
    /**
301
     * @param array $needsUpdate
302
     */
303
    protected function _outputUpdateInformation(array $needsUpdate)
304
    {
305
        $output = $this->_output;
306
        foreach ($needsUpdate as $name => $setupResource) {
307
            $dbVersion = $this->_getDbVersionFromName($name);
308
            $dbDataVersion = $this->_getDbDataVersionFromName($name);
309
            $configVersion = $this->_getConfiguredVersionFromResourceObject($setupResource);
310
311
            $moduleConfig = $this->_getProtectedPropertyFromObject('_moduleConfig', $setupResource);
312
            $output->writeln(
313
                array(
314
                    '+--------------------------------------------------+',
315
                    'Resource Name:             ' . $name,
316
                    'For Module:                ' . $moduleConfig->getName(),
317
                    'Class:                     ' . get_class($setupResource),
318
                    'Current Structure Version: ' . $dbVersion,
319
                    'Current Data Version:      ' . $dbDataVersion,
320
                    'Configured Version:        ' . $configVersion,
321
                )
322
            );
323
324
            $args = array(
325
                '',
326
                (string) $dbVersion,
327
                (string) $configVersion,
328
            );
329
330
            $args[0] = $dbVersion
331
                ? \Mage_Core_Model_Resource_Setup::TYPE_DB_UPGRADE
332
                : \Mage_Core_Model_Resource_Setup::TYPE_DB_INSTALL;
333
            $output->writeln('Structure Files to Run: ');
334
            $filesStructure = $this->_getAvaiableDbFilesFromResource($setupResource, $args);
335
            $this->_outputFileArray($filesStructure, $output);
0 ignored issues
show
Unused Code introduced by
The call to IncrementalCommand::_outputFileArray() has too many arguments starting with $output.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
336
            $output->writeln("");
337
338
            $args[0] = $dbVersion
339
                ? \Mage_Core_Model_Resource_Setup::TYPE_DATA_UPGRADE
340
                : \Mage_Core_Model_Resource_Setup::TYPE_DATA_INSTALL;
341
            $output->writeln('Data Files to Run: ');
342
            $filesData = $this->_getAvaiableDataFilesFromResource($setupResource, $args);
343
            $this->_outputFileArray($filesData, $output);
0 ignored issues
show
Unused Code introduced by
The call to IncrementalCommand::_outputFileArray() has too many arguments starting with $output.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
344
            $output->writeln('+--------------------------------------------------+');
345
            $output->writeln('');
346
        }
347
    }
348
349
    /**
350
     * @param array $files
351
     */
352
    protected function _outputFileArray($files)
353
    {
354
        $output = $this->_output;
355
        if (count($files) == 0) {
356
            $output->writeln('No files found');
357
358
            return;
359
        }
360
        foreach ($files as $file) {
361
            $output->writeln(str_replace(\Mage::getBaseDir() . '/', '', $file['fileName']));
362
        }
363
    }
364
365
    /**
366
     * Runs a single named setup resource
367
     *
368
     * This method nukes the global/resources node in the global config
369
     * and then repopulates it with **only** the $name resource. Then it
370
     * calls the standard Magento `applyAllUpdates` method.
371
     *
372
     * The benefit of this approach is we don't need to recreate the entire
373
     * setup resource running logic ourselves.  Yay for code reuse
374
     *
375
     * The downside is we should probably exit quickly, as anything else that
376
     * uses the global/resources node is going to behave weird.
377
     *
378
     * @todo     Repopulate global config after running?  Non trivial since setNode escapes strings
379
     *
380
     * @param string $name
381
     * @param array $needsUpdate
382
     * @param string $type
383
     *
384
     * @throws RuntimeException
385
     * @internal param $string
386
     */
387
    protected function _runNamedSetupResource($name, array $needsUpdate, $type)
388
    {
389
        $output = $this->_output;
390
        if (!in_array($type, array(self::TYPE_MIGRATION_STRUCTURE, self::TYPE_MIGRATION_DATA))) {
391
            throw new RuntimeException('Invalid Type [' . $type . ']: structure, data is valid');
392
        }
393
394
        if (!array_key_Exists($name, $needsUpdate)) {
395
            $output->writeln('<error>No updates to run for ' . $name . ', skipping </error>');
396
397
            return;
398
        }
399
400
        //remove all other setup resources from configuration
401
        //(in memory, do not persist this to cache)
402
        $realConfig = \Mage::getConfig();
403
        $resources = $realConfig->getNode('global/resources');
404
        foreach ($resources->children() as $resource) {
405
            if (!$resource->setup) {
406
                continue;
407
            }
408
            unset($resource->setup);
409
        }
410
        //recreate our specific node in <global><resources></resource></global>
411
        //allows for theoretical multiple runs
412
        $setupResourceConfig = $this->_secondConfig->getNode('global/resources/' . $name);
413
        $moduleName = $setupResourceConfig->setup->module;
414
        $className = $setupResourceConfig->setup->class;
415
416
        $specificResource = $realConfig->getNode('global/resources/' . $name);
417
        $setup = $specificResource->addChild('setup');
418
        if ($moduleName) {
419
            $setup->addChild('module', $moduleName);
420
        } else {
421
            $output->writeln(
422
                '<error>No module node configured for ' . $name . ', possible configuration error </error>'
423
            );
424
        }
425
426
        if ($className) {
427
            $setup->addChild('class', $className);
428
        }
429
430
        //and finally, RUN THE UPDATES
431
        try {
432
            ob_start();
433
            if ($type == self::TYPE_MIGRATION_STRUCTURE) {
434
                $this->_stashEventContext();
435
                \Mage_Core_Model_Resource_Setup::applyAllUpdates();
436
                $this->_restoreEventContext();
437
            } else {
438
                if ($type == self::TYPE_MIGRATION_DATA) {
439
                    \Mage_Core_Model_Resource_Setup::applyAllDataUpdates();
440
                }
441
            }
442
            $exceptionOutput = ob_get_clean();
443
            $this->_output->writeln($exceptionOutput);
444
        } catch (Exception $e) {
445
            $exceptionOutput = ob_get_clean();
446
            $this->_processExceptionDuringUpdate($e, $name, $exceptionOutput);
447
            if ($this->_input->getOption('stop-on-error')) {
448
                throw new RuntimeException('Setup stopped with errors');
449
            }
450
        }
451
    }
452
453
    /**
454
     * @param Exception $e
455
     * @param string $name
456
     * @param string $magentoExceptionOutput
457
     */
458
    protected function _processExceptionDuringUpdate(
459
        Exception $e,
460
        $name,
461
        $magentoExceptionOutput
462
    ) {
463
        $output = $this->_output;
464
        $output->writeln(array(
465
            "<error>Magento encountered an error while running the following setup resource.</error>",
466
            "",
467
            "    $name ",
468
            "",
469
            "<error>The Good News:</error> You know the error happened, and the database",
470
            "information below will  help you fix this error!",
471
            "",
472
            "<error>The Bad News:</error> Because Magento/MySQL can't run setup resources",
473
            "transactionally your database is now in an half upgraded, invalid",
474
            "state. Even if you fix the error, new errors may occur due to",
475
            "this half upgraded, invalid state.",
476
            '',
477
            "What to Do: ",
478
            "1. Figure out why the error happened, and manually fix your",
479
            "   database and/or system so it won't happen again.",
480
            "2. Restore your database from backup.",
481
            "3. Re-run the scripts.",
482
            "",
483
            "Exception Message:",
484
            $e->getMessage(),
485
            "",
486
        ));
487
488
        if ($magentoExceptionOutput) {
489
            /* @var  $dialog DialogHelper */
490
            $dialog = $this->getHelper('dialog');
491
            $dialog->ask(
492
                $output,
493
                '<question>Press Enter to view raw Magento error text:</question> '
494
            );
495
            $output->writeln("Magento Exception Error Text:");
496
            echo $magentoExceptionOutput, "\n"; //echoing (vs. writeln) to avoid seg fault
497
        }
498
    }
499
500
    /**
501
     * @return bool
502
     */
503
    protected function _checkCacheSettings()
504
    {
505
        $output = $this->_output;
506
        $allTypes = \Mage::app()->useCache();
507
        if ($allTypes['config'] !== '1') {
508
            $output->writeln('<error>ERROR: Config Cache is Disabled</error>');
509
            $output->writeln('This command will not run with the configuration cache disabled.');
510
            $output->writeln('Please change your Magento settings at System -> Cache Management');
511
            $output->writeln('');
512
513
            return false;
514
        }
515
516
        return true;
517
    }
518
519
    /**
520
     * @param string $toUpdate
521
     * @param array $needsUpdate
522
     * @param string $type
523
     */
524
    protected function _runStructureOrDataScripts($toUpdate, array $needsUpdate, $type)
525
    {
526
        $output = $this->_output;
527
        $output->writeln('The next ' . $type . ' update to run is <info>' . $toUpdate . '</info>');
528
        /* @var  $dialog DialogHelper */
529
        $dialog = $this->getHelper('dialog');
530
        $dialog->ask(
531
            $output,
532
            '<question>Press Enter to Run this update: </question>'
533
        );
534
535
        $start = microtime(true);
536
        $this->_runNamedSetupResource($toUpdate, $needsUpdate, $type);
537
        $time_ran = microtime(true) - $start;
538
        $output->writeln('');
539
        $output->writeln(ucwords($type) . ' update <info>' . $toUpdate . '</info> complete.');
540
        $output->writeln('Ran in ' . floor($time_ran * 1000) . 'ms');
541
    }
542
543
    /**
544
     * @return array
545
     */
546
    protected function _getTestedVersions()
547
    {
548
        return $this->_config['tested-versions'];
549
    }
550
551
    protected function _restoreEventContext()
552
    {
553
        $app = \Mage::app();
554
        $this->_setProtectedPropertyFromObjectToValue('_events', $app, $this->_eventStash);
555
    }
556
557
    protected function _stashEventContext()
558
    {
559
        $app = \Mage::app();
560
        $events = $this->_getProtectedPropertyFromObject('_events', $app);
561
        $this->_eventStash = $events;
562
        $this->_setProtectedPropertyFromObjectToValue('_events', $app, array());
563
    }
564
565
    /**
566
     * @return bool
567
     */
568
    protected function _init()
569
    {
570
        //bootstrap magento
571
        $this->detectMagento($this->_output);
572
        if (!$this->initMagento()) {
573
            return false;
574
        }
575
576
        //don't run if cache is off.  If cache is off that means
577
        //setup resource will run automagically
578
        if (!$this->_checkCacheSettings()) {
579
            return false;
580
        }
581
582
        //load a second, not cached, config.xml tree
583
        $this->_loadSecondConfig();
584
585
        return true;
586
    }
587
588
    /**
589
     * @return array
590
     */
591
    protected function _analyzeSetupResourceClasses()
592
    {
593
        $output = $this->_output;
594
        $this->writeSection($output, 'Analyzing Setup Resource Classes');
595
        $setupResources = $this->_getAllSetupResourceObjects();
596
        $needsUpdate = $this->_getAllSetupResourceObjectThatNeedUpdates($setupResources);
597
598
        $output->writeln(
599
            'Found <info>' . count($setupResources) . '</info> configured setup resource(s)</info>'
600
        );
601
        $output->writeln(
602
            'Found <info>' . count($needsUpdate) . '</info> setup resource(s) which need an update</info>'
603
        );
604
605
        return $needsUpdate;
606
    }
607
608
    /**
609
     * @param array $needsUpdate
610
     */
611
    protected function _listDetailedUpdateInformation(array $needsUpdate)
612
    {
613
        $output = $this->_output;
614
        /* @var  $dialog DialogHelper */
615
        $dialog = $this->getHelper('dialog');
616
        $dialog->ask(
617
            $output,
618
            '<question>Press Enter to View Update Information: </question>'
619
        );
620
621
        $this->writeSection($output, 'Detailed Update Information');
622
        $this->_outputUpdateInformation($needsUpdate, $output);
0 ignored issues
show
Unused Code introduced by
The call to IncrementalCommand::_outputUpdateInformation() has too many arguments starting with $output.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
623
    }
624
625
    /**
626
     * @param array $needsUpdate
627
     */
628
    protected function _runAllStructureUpdates(array $needsUpdate)
629
    {
630
        $output = $this->_output;
631
        $this->writeSection($output, "Run Structure Updates");
632
        $output->writeln('All structure updates run before data updates.');
633
        $output->writeln('');
634
635
        $c = 1;
636
        $total = count($needsUpdate);
637 View Code Duplication
        foreach ($needsUpdate as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
638
            $toUpdate = $key;
639
            $this->_runStructureOrDataScripts($toUpdate, $needsUpdate, self::TYPE_MIGRATION_STRUCTURE);
640
            $output->writeln("($c of $total)");
641
            $output->writeln('');
642
            $c++;
643
        }
644
645
        $this->writeSection($output, "Run Data Updates");
646
        $c = 1;
647
        $total = count($needsUpdate);
648 View Code Duplication
        foreach ($needsUpdate as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
649
            $toUpdate = $key;
650
            $this->_runStructureOrDataScripts($toUpdate, $needsUpdate, self::TYPE_MIGRATION_DATA);
651
            $output->writeln("($c of $total)");
652
            $output->writeln('');
653
            $c++;
654
        }
655
    }
656
}
657