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) { |
|
|
|
|
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) { |
|
|
|
|
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
|
|
|
|
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.