Completed
Push — ezp25946-migrate_files_to_othe... ( 5c47cf...c757fe )
by
unknown
13:02
created

MigrateFilesCommand::migrateFiles()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 23
nc 4
nop 4
dl 0
loc 38
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the MigrateFilesCommand class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Bundle\EzPublishIOBundle\Command;
10
11
use eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandlerInterface;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Helper\ProgressBar;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Question\ConfirmationQuestion;
18
19
class MigrateFilesCommand extends Command
20
{
21
    private $configuredMetadataHandlers;
22
23
    private $configuredBinarydataHandlers;
24
25
    /** @var \eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandlerInterface */
26
    private $migrationHandler;
27
28
    public function __construct(
29
        $configuredMetadataHandlers,
30
        $configuredBinarydataHandlers,
31
        MigrationHandlerInterface $migrationHandler
32
    ) {
33
        $this->configuredMetadataHandlers = $configuredMetadataHandlers;
34
        $this->configuredBinarydataHandlers = $configuredBinarydataHandlers;
35
        $this->configuredMetadataHandlers['default'] = [];
36
        $this->configuredBinarydataHandlers['default'] = [];
37
38
        $this->migrationHandler = $migrationHandler;
39
40
        parent::__construct();
41
    }
42
43
    protected function configure()
44
    {
45
        $this
46
            ->setName('ezplatform:io:migrate-files')
47
            ->setDescription('Migrates files from one IO repository to another')
48
            ->addOption('from', null, InputOption::VALUE_REQUIRED, 'Migrate from <from_metadata_handler>,<from_binarydata_handler>')
49
            ->addOption('to', null, InputOption::VALUE_REQUIRED, 'Migrate to <to_metadata_handler>,<to_binarydata_handler>')
50
            ->addOption('list-io-configs', null, InputOption::VALUE_NONE, 'List available IO configurations')
51
            ->addOption('remove-files', null, InputOption::VALUE_NONE, 'Remove source files after copying')
52
            ->addOption('bulk-count', null, InputOption::VALUE_REQUIRED, 'Number of files processed at once', 100)
53
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute a dry run')
54
            ->setHelp(
55
                <<<EOT
56
The command <info>%command.name%</info> migrates files from one IO repository
57
to another.
58
59
It can for example be used to migrate local files from the default IO
60
configuration to a new IO configuration, like a clustered setup.
61
62
The <info>--from</info> and <info>--to</info> values must be specified as <info><metadata_handler>,<binarydata_handler></info>.
63
If <info>--from</info> is omitted, the default IO configuration will be used.
64
If <info>--to</info> is omitted, the first non-default IO configuration will be used.
65
66
<fg=red>During the script execution the files should not be modified. To avoid
67
surprises you are advised to create a backup and/or execute a dry run before
68
proceeding with actual update.</>
69
70
Since this script can potentially run for a very long time, to avoid memory
71
exhaustion run it in production environment using <info>--env=prod</info> switch.
72
73
EOT
74
            );
75
    }
76
77
    protected function execute(InputInterface $input, OutputInterface $output)
78
    {
79
        if ($input->getOption('list-io-configs')) {
80
            $this->outputConfiguredHandlers($output);
81
82
            return;
83
        }
84
85
        $output->writeln($this->getProcessedHelp());
86
87
        $fromHandlers = $input->getOption('from') ? explode(',', $input->getOption('from')) : null;
88
        $toHandlers = $input->getOption('to') ? explode(',', $input->getOption('to')) : null;
89
        if (!$this->areHandlerOptionsValid($fromHandlers, $toHandlers, $output)) {
90
            return;
91
        }
92
93
        if (!$fromHandlers) {
94
            $fromHandlers = ['default', 'default'];
95
        }
96
        if (!$toHandlers) {
97
            $toHandlers = [
98
                array_keys($this->configuredMetadataHandlers)[0],
99
                array_keys($this->configuredBinarydataHandlers)[0],
100
            ];
101
        }
102
103
        $output->writeln([
104
            "Migrating from '$fromHandlers[0],$fromHandlers[1]' to '$toHandlers[0],$toHandlers[1]'",
105
            '',
106
        ]);
107
        $this->migrationHandler->setIODataHandlersByIdentifiers(
108
            $fromHandlers[0],
109
            $fromHandlers[1],
110
            $toHandlers[0],
111
            $toHandlers[1]
112
        );
113
114
        $totalCount = $this->migrationHandler->countFiles();
115
        $output->writeln([
116
            'Found total files to update: ' . $totalCount,
117
            '',
118
        ]);
119
120
        if ($totalCount === 0) {
121
            $output->writeln('Nothing to process.');
122
123
            return;
124
        }
125
126
        $helper = $this->getHelper('question');
127
        $question = new ConfirmationQuestion(
128
            '<question>Are you sure you want to proceed?</question> ',
129
            false
130
        );
131
132
        if (!$helper->ask($input, $output, $question)) {
133
            $output->writeln('Aborting.');
134
135
            return;
136
        }
137
138
        $bulkCount = $input->getOption('bulk-count');
139
        $dryRun = $input->getOption('dry-run');
140
141
        $this->migrateFiles(
142
            $totalCount,
143
            $bulkCount,
144
            $dryRun,
145
            $output
146
        );
147
    }
148
149
    /**
150
     * Output the configured meta/binary data handlers.
151
     *
152
     * @param OutputInterface $output
153
     */
154
    protected function outputConfiguredHandlers(OutputInterface $output)
155
    {
156
        $output->writeln(
157
            'Configured meta data handlers: ' . implode(', ', array_keys($this->configuredMetadataHandlers))
158
        );
159
        $output->writeln(
160
            'Configured binary data handlers: ' . implode(', ', array_keys($this->configuredBinarydataHandlers))
161
        );
162
    }
163
164
    /**
165
     * Verify that the handler options have been set to meaningful values.
166
     *
167
     * @param mixed $fromHandlers
168
     * @param mixed $toHandlers
169
     * @param OutputInterface $output
170
     * @return bool
171
     */
172
    protected function areHandlerOptionsValid(
173
        $fromHandlers,
174
        $toHandlers,
175
        OutputInterface $output
176
    ) {
177 View Code Duplication
        if ($fromHandlers) {
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...
178
            if (count($fromHandlers) !== 2) {
179
                $output->writeln('Enter two comma separated values for the --from option: <from_metadata_handler>,<from_binarydata_handler>');
180
181
                return false;
182
            }
183
184
            if (!in_array($fromHandlers[0], array_keys($this->configuredMetadataHandlers))) {
185
                $output->writeln("From meta data handler '$fromHandlers[0]' is not configured.");
186
                $this->outputConfiguredHandlers($output);
187
188
                return false;
189
            }
190
191
            if (!in_array($fromHandlers[1], array_keys($this->configuredBinarydataHandlers))) {
192
                $output->writeln("From binary data handler '$fromHandlers[1]' is not configured.");
193
                $this->outputConfiguredHandlers($output);
194
195
                return false;
196
            }
197
        }
198
199 View Code Duplication
        if ($toHandlers) {
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...
200
            if (count($toHandlers) !== 2) {
201
                $output->writeln('Enter two comma separated values for the --to option: <to_metadata_handler>,<to_binarydata_handler>');
202
203
                return false;
204
            }
205
206
            if (!in_array($toHandlers[0], array_keys($this->configuredMetadataHandlers))) {
207
                $output->writeln("To meta data handler '$toHandlers[0]' is not configured.");
208
                $this->outputConfiguredHandlers($output);
209
210
                return false;
211
            }
212
213
            if (!in_array($toHandlers[1], array_keys($this->configuredBinarydataHandlers))) {
214
                $output->writeln("To binary data handler '$toHandlers[1]' is not configured.");
215
                $this->outputConfiguredHandlers($output);
216
217
                return false;
218
            }
219
        }
220
221
        if ($fromHandlers && $toHandlers && $fromHandlers === $toHandlers) {
222
            $output->writeln('From and to handlers are the same. Nothing to do.');
223
224
            return false;
225
        }
226
227
        return true;
228
    }
229
230
    /**
231
     * Migrate files.
232
     *
233
     * @param int $fileCount
234
     * @param int $bulkCount
235
     * @param bool $dryRun
236
     * @param OutputInterface $output
237
     */
238
    protected function migrateFiles(
239
        $fileCount,
240
        $bulkCount,
241
        $dryRun,
242
        OutputInterface $output
243
    ) {
244
        $passCount = ceil($fileCount / $bulkCount);
245
246
        $progress = new ProgressBar($output, $fileCount);
247
        $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%');
248
249
        $output->writeln('');
250
        $progress->start();
251
252
        for ($pass = 0; $pass <= $passCount; ++$pass) {
253
            $metadataList = $this->migrationHandler->loadMetadataList($bulkCount, $pass * $bulkCount);
254
255
            foreach ($metadataList as $metadata) {
256
                if (!$dryRun) {
257
                    $this->migrationHandler->migrateFile($metadata);
258
                }
259
260
                $progress->clear();
261
262
                $output->write("\r");
263
                $output->writeln('Updated file ' . $metadata->id);
264
                $output->write("\r");
265
266
                $progress->display();
267
268
                $progress->advance();
269
            }
270
        }
271
272
        $progress->finish();
273
274
        $output->writeln('');
275
    }
276
}
277