Completed
Push — EZP-26146-location-swap-urlali... ( 592ac4...8ddb19 )
by
unknown
25:53
created

backupCustomLocationAliases()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 0
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the eZ Publish Kernel package.
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
 * @version //autogentag//
10
 */
11
namespace eZ\Bundle\EzPublishMigrationBundle\Command\LegacyStorage;
12
13
use eZ\Publish\API\Repository\Exceptions\ForbiddenException;
14
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway;
15
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler as UrlAliasHandler;
16
use eZ\Publish\Core\Persistence\Legacy\Handler as LegacyStorageEngine;
17
use Doctrine\DBAL\Query\QueryBuilder;
18
use Doctrine\DBAL\Schema\Schema;
19
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Helper\ProgressBar;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use RuntimeException;
25
use Exception;
26
use PDO;
27
28
class RegenerateUrlAliasesCommand extends ContainerAwareCommand
29
{
30
    const MIGRATION_TABLE = '__migration_ezurlalias_ml';
31
    const CUSTOM_ALIAS_BACKUP_TABLE = '__migration_backup_custom_alias';
32
33
    /**
34
     * @var \eZ\Publish\API\Repository\ContentService
35
     */
36
    protected $contentService;
37
38
    /**
39
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
40
     */
41
    protected $nameSchemaResolver;
42
43
    /**
44
     * @var \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler
45
     */
46
    protected $urlAliasHandler;
47
48
    /**
49
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway
50
     */
51
    protected $urlAliasGateway;
52
53
    /**
54
     * @var \Doctrine\DBAL\Connection $connection
55
     */
56
    protected $connection;
57
58
    /**
59
     * @var int
60
     */
61
    protected $bulkCount;
62
63
    /**
64
     * @var \Symfony\Component\Console\Output\OutputInterface
65
     */
66
    protected $output;
67
68
    protected $actions = [
69
        'full',
70
        'autogenerate',
71
        'backup-custom',
72
        'restore-custom',
73
    ];
74
75
    protected function configure()
76
    {
77
        $this
78
            ->setName('ezpublish:regenerate:legacy_storage_url_aliases')
79
            ->setDescription('Updates sort keys in configured Legacy Storage database')
80
            ->addArgument(
81
                'action',// regenerate
82
                InputArgument::REQUIRED,
83
                'Action to perform, one of: full, autogenerate, backup-custom, restore-custom'
84
            )
85
            ->addArgument(
86
                'bulk-count',
87
                InputArgument::OPTIONAL,
88
                'Number of items (Locations, URL aliases) processed at once',
89
                50
90
            )
91
            ->setHelp(
92
                <<<EOT
93
The command <info>%command.name%</info>
94
95
<warning>During the script execution the database should not be modified.
96
97
Since this script can potentially run for a very long time, to avoid memory
98
exhaustion run it in production environment using <info>--env=prod</info> switch.
99
100
EOT
101
            );
102
    }
103
104
    protected function execute(InputInterface $input, OutputInterface $output)
105
    {
106
        $this->checkStorage();
107
        $this->prepareDependencies($output);
108
109
        $action = $input->getArgument('action');
110
        $this->bulkCount = $input->getArgument('bulk-count');
111
112
        if (!in_array($action, $this->actions)) {
113
            throw new RuntimeException("Action '{$action}' is not supported");
114
        }
115
116
        if ($action === 'full' || $action === 'backup-custom')
117
        {
118
            $this->backupCustomLocationAliases();
119
        }
120
121
        if ($action === 'full' || $action === 'autogenerate')
122
        {
123
            $this->generateLocationAliases();
124
        }
125
126
        if ($action === 'full' || $action === 'restore-custom')
127
        {
128
            $this->restoreCustomLocationAliases();
129
        }
130
    }
131
132
    /**
133
     * Checks that configured storage engine is Legacy Storage Engine.
134
     */
135
    protected function checkStorage()
136
    {
137
        $storageEngine = $this->getContainer()->get('ezpublish.api.storage_engine');
138
139
        if (!$storageEngine instanceof LegacyStorageEngine) {
140
            throw new RuntimeException(
141
                'Expected to find Legacy Storage Engine but found something else.'
142
            );
143
        }
144
    }
145
146
    /**
147
     * Prepares dependencies used by the command.
148
     *
149
     * @param \Symfony\Component\Console\Output\OutputInterface $output
150
     */
151
    protected function prepareDependencies(OutputInterface $output)
152
    {
153
        /** @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler $databaseHandler */
154
        $databaseHandler = $this->getContainer()->get('ezpublish.connection');
155
        /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $gateway */
156
        $gateway = $this->getContainer()->get('ezpublish.persistence.legacy.url_alias.gateway');
157
        /** @var \eZ\Publish\SPI\Persistence\Handler $persistenceHandler */
158
        $persistenceHandler = $this->getContainer()->get('ezpublish.api.persistence_handler');
159
        /** @var \eZ\Publish\Core\Repository\Repository $innerRepository */
160
        $innerRepository = $this->getContainer()->get('ezpublish.api.inner_repository');
161
        /** @var \eZ\Publish\API\Repository\Repository $repository */
162
        $repository = $this->getContainer()->get('ezpublish.api.repository');
163
164
        $administratorUser = $repository->getUserService()->loadUser(14);
165
        $repository->getPermissionResolver()->setCurrentUserReference($administratorUser);
166
167
        $this->contentService = $repository->getContentService();
168
        $this->nameSchemaResolver = $innerRepository->getNameSchemaService();
169
        $this->urlAliasHandler = $persistenceHandler->urlAliasHandler();
170
        $this->urlAliasGateway = $gateway;
171
        $this->connection = $databaseHandler->getConnection();
172
        $this->output = $output;
173
    }
174
175
    /**
176
     * Sets storage gateway to the default table.
177
     *
178
     * @see \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway::TABLE
179
     */
180
    protected function setDefaultTable()
181
    {
182
        $this->urlAliasGateway->setTable(UrlAliasGateway::TABLE);
183
    }
184
185
    /**
186
     * Sets storage gateway to the migration table.
187
     *
188
     * @see \eZ\Bundle\EzPublishMigrationBundle\Command\LegacyStorage\RegenerateUrlAliasesCommand::MIGRATION_TABLE
189
     */
190
    protected function setMigrationTable()
191
    {
192
        $this->urlAliasGateway->setTable(static::MIGRATION_TABLE);
193
    }
194
195
    /**
196
     * Backups custom URL aliases the custom URL alias backup table.
197
     */
198
    protected function backupCustomLocationAliases()
199
    {
200
        if (!$this->tableExists(static::CUSTOM_ALIAS_BACKUP_TABLE)) {
201
            $this->createCustomAliasBackupTable();
202
        }
203
204
        if (!$this->isTableEmpty(static::CUSTOM_ALIAS_BACKUP_TABLE)) {
205
            throw new RuntimeException(
206
                'Table ' . static::CUSTOM_ALIAS_BACKUP_TABLE . ' contains data. ' .
207
                "Ensure it's empty or non-existent (it will be automatically created)."
208
            );
209
        }
210
211
        $this->doBackupCustomLocationAliases();
212
    }
213
214
    /**
215
     * Internal method for backing up custom URL aliases.
216
     *
217
     * @see \eZ\Bundle\EzPublishMigrationBundle\Command\LegacyStorage\RegenerateUrlAliasesCommand::backupCustomLocationAliases()
218
     */
219
    protected function doBackupCustomLocationAliases()
220
    {
221
        $totalCount = $this->getTotalLocationCount();
222
        $passCount = ceil($totalCount / $this->bulkCount);
223
        $customAliasCount = 0;
224
        $customAliasPathCount = 0;
225
226
        $queryBuilder = $this->connection->createQueryBuilder();
227
        $queryBuilder
228
            ->select('node_id', 'parent_node_id', 'contentobject_id')
229
            ->from('ezcontentobject_tree')
230
            ->where($queryBuilder->expr()->neq('node_id', 1))
231
            ->orderBy('depth', 'ASC')
232
            ->orderBy('node_id', 'ASC');
233
234
        $this->output->writeln("Backing up custom URL aliases for {$totalCount} Location(s).");
235
236
        $progressBar = $this->getProgressBar($totalCount);
237
        $progressBar->start();
238
239
        for ($pass = 0; $pass <= $passCount; ++$pass) {
240
            $rows = $this->loadLocationData($queryBuilder, $pass);
241
242
            foreach ($rows as $row) {
243
                $customAliases = $this->urlAliasHandler->listURLAliasesForLocation(
244
                    $row['node_id'],
245
                    true
246
                );
247
248
                $customAliasCount += count($customAliases);
249
                $customAliasPathCount += $this->storeCustomAliases($customAliases);
250
251
            }
252
253
            $progressBar->advance(count($rows));
254
        }
255
256
        $progressBar->finish();
257
258
        $this->output->writeln('');
259
        $this->output->writeln(
260
            "Done. Backed up {$customAliasCount} custom URL alias(es) " .
261
            "with {$customAliasPathCount} path(s)."
262
        );
263
        $this->output->writeln('');
264
    }
265
266
    /**
267
     * Restores custom URL aliases from the backup table.
268
     */
269
    protected function restoreCustomLocationAliases()
270
    {
271
        if (!$this->tableExists(static::MIGRATION_TABLE)) {
272
            throw new RuntimeException(
273
                'Could not find custom URL alias backup table ' . static::MIGRATION_TABLE . '. ' .
274
                'Ensure that table is created by backup process.'
275
            );
276
        }
277
278
        $this->doRestoreCustomLocationAliases();
279
    }
280
281
    /**
282
     * Restores custom URL aliases from the backup table.
283
     *
284
     * @see \eZ\Bundle\EzPublishMigrationBundle\Command\LegacyStorage\RegenerateUrlAliasesCommand::restoreCustomLocationAliases()
285
     */
286
    protected function doRestoreCustomLocationAliases()
287
    {
288
        $totalCount = $this->getTotalCustomUrlAliasBackupCount();
289
        $passCount = ceil($totalCount / $this->bulkCount);
290
        $createdAliasCount = 0;
291
        $conflictCount = 0;
292
293
        $queryBuilder = $this->connection->createQueryBuilder();
294
        $queryBuilder
295
            ->select('*')
296
            ->from(static::CUSTOM_ALIAS_BACKUP_TABLE)
297
            ->orderBy('id', 'ASC');
298
299
        $this->output->writeln("Restoring {$totalCount} custom URL alias(es).");
300
301
        $progressBar = $this->getProgressBar($totalCount);
302
        $progressBar->start();
303
304
        for ($pass = 0; $pass <= $passCount; ++$pass) {
305
            $rows = $this->loadCustomUrlAliasData($queryBuilder, $pass);
306
307
            foreach ($rows as $row) {
308
                try {
309
                    $this->setMigrationTable();
310
                    $this->urlAliasHandler->createCustomUrlAlias(
311
                        $row['location_id'],
312
                        $row['path'],
313
                        (bool)$row['forwarding'],
314
                        $row['language_code'],
315
                        (bool)$row['always_available']
316
                    );
317
                    $createdAliasCount += 1;
318
                    $this->setDefaultTable();
319
                } catch (ForbiddenException $e) {
320
                    $conflictCount += 1;
321
                } catch (Exception $e) {
322
                    $this->setDefaultTable();
323
                    throw $e;
324
                }
325
            }
326
327
            $progressBar->advance(count($rows));
328
        }
329
330
        $progressBar->finish();
331
332
        $this->output->writeln('');
333
        $this->output->writeln(
334
            "Done. Restored {$createdAliasCount} custom URL alias(es) " .
335
            "with {$conflictCount} conflict(s)."
336
        );
337
        $this->output->writeln('');
338
    }
339
340
    /**
341
     * Loads Location data for the given $pass.
342
     *
343
     * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder
344
     * @param int $pass
345
     *
346
     * @return array
347
     */
348 View Code Duplication
    protected function loadCustomUrlAliasData(QueryBuilder $queryBuilder, $pass)
349
    {
350
        $queryBuilder->setFirstResult($pass * $this->bulkCount);
351
        $queryBuilder->setMaxResults($this->bulkCount);
352
353
        $statement = $queryBuilder->execute();
354
355
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
356
357
        return $rows;
358
    }
359
360
    /**
361
     * Stores given custom $aliases to the custom alias backup table.
362
     *
363
     * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias[] $aliases
364
     *
365
     * @return int
366
     */
367
    protected function storeCustomAliases(array $aliases)
368
    {
369
        $pathCount = 0;
370
371
        foreach ($aliases as $alias) {
372
            $paths = $this->combinePaths($alias->pathData);
373
            $pathCount += count($paths);
374
375
            foreach ($paths as $path) {
376
                $this->storeCustomAliasPath(
377
                    $alias->destination,
378
                    $path,
379
                    reset($alias->languageCodes),
380
                    $alias->alwaysAvailable,
381
                    $alias->forward
382
                );
383
            }
384
        }
385
386
        return $pathCount;
387
    }
388
389
    /**
390
     * Stores custom URL alias data for $path to the backup table.
391
     *
392
     * @param int $locationId
393
     * @param string $path
394
     * @param string $languageCode
395
     * @param boolean $alwaysAvailable
396
     * @param boolean $forwarding
397
     */
398
    protected function storeCustomAliasPath($locationId, $path, $languageCode, $alwaysAvailable, $forwarding)
399
    {
400
        $queryBuilder = $this->connection->createQueryBuilder();
401
402
        $queryBuilder->insert(static::CUSTOM_ALIAS_BACKUP_TABLE);
403
        $queryBuilder->values(
404
            [
405
                'id' => '?',
406
                'location_id' => '?',
407
                'path' => '?',
408
                'language_code' => '?',
409
                'always_available' => '?',
410
                'forwarding' => '?',
411
            ]
412
        );
413
        $queryBuilder->setParameter(0, 0);
414
        $queryBuilder->setParameter(1, $locationId);
415
        $queryBuilder->setParameter(2, $path);
416
        $queryBuilder->setParameter(3, $languageCode);
417
        $queryBuilder->setParameter(4, (int)$alwaysAvailable);
418
        $queryBuilder->setParameter(5, (int)$forwarding);
419
420
        $queryBuilder->execute();
421
    }
422
423
    /**
424
     * Combines path data to an array of URL alias paths.
425
     *
426
     * Explanation:
427
     *
428
     * Custom URL aliases can generate NOP entries, which can be taken over by the autogenerated
429
     * aliases. When multiple languages exists for the Location that took over, multiple entries
430
     * with the same link will exist on the same level. In that case it will not be possible to
431
     * reliably reconstruct what was the path for the original custom alias. For that reason we
432
     * combine path data to get all possible path combinations.
433
     *
434
     * Note: it could happen that original NOP entry was historized after being taken over by the
435
     * autogenerated alias. So to be complete this would have to take into account history entries
436
     * as well, but at the moment we lack API to do that.
437
     *
438
     * Proper solution of this problem would be introducing separate database table to store custom
439
     * URL alias data.
440
     *
441
     * @see https://jira.ez.no/browse/EZP-20777
442
     *
443
     * @param array $pathData
444
     *
445
     * @return string[]
446
     */
447
    protected function combinePaths(array $pathData)
448
    {
449
        $paths = [];
450
        $levelData = array_shift($pathData);
451
        $levelElements = $this->extractPathElements($levelData);
452
453
        if (!empty($pathData)) {
454
            $nextElements = $this->combinePaths($pathData);
455
456
            foreach ($levelElements as $element1) {
457
                foreach ($nextElements as $element2) {
458
                    $paths[] = $element1 . '/' . $element2;
459
                }
460
            }
461
462
            return $paths;
463
        }
464
465
        return $levelElements;
466
    }
467
468
    /**
469
     * Returns all path element strings found for the given path $levelData.
470
     *
471
     * @param array $levelData
472
     *
473
     * @return string[]
474
     */
475
    protected function extractPathElements(array $levelData)
476
    {
477
        $elements = [];
478
479
        if (isset($levelData['translations']['always-available'])) {
480
            // NOP entry
481
            $elements[] = $levelData['translations']['always-available'];
482
        } else {
483
            // Language(s) entry
484
            $elements = array_values($levelData['translations']);
485
        }
486
487
        return $elements;
488
    }
489
490
    /**
491
     * Generates URL aliases from the Location and Content data to the migration table.
492
     */
493
    protected function generateLocationAliases()
494
    {
495
        $tableName = static::MIGRATION_TABLE;
496
497
        if (!$this->tableExists($tableName)) {
498
            throw new RuntimeException("Table '{$tableName}' does not exist.");
499
        }
500
501
        if (!$this->isTableEmpty($tableName)) {
502
            throw new RuntimeException("Table '{$tableName}' contains data.");
503
        }
504
505
        $this->doGenerateLocationAliases();
506
    }
507
508
    /**
509
     * Internal method for generating URL aliases.
510
     *
511
     * @see \eZ\Bundle\EzPublishMigrationBundle\Command\LegacyStorage\RegenerateUrlAliasesCommand::generateLocationAliases()
512
     */
513
    protected function doGenerateLocationAliases()
514
    {
515
        $totalLocationCount = $this->getTotalLocationCount();
516
        $totalContentCount = $this->getTotalLocationContentCount();
517
        $passCount = ceil($totalLocationCount / $this->bulkCount);
518
        $publishedAliasCount = 0;
519
520
        $queryBuilder = $this->connection->createQueryBuilder();
521
        $queryBuilder
522
            ->select('node_id', 'parent_node_id', 'contentobject_id')
523
            ->from('ezcontentobject_tree')
524
            ->where($queryBuilder->expr()->neq('node_id', 1))
525
            ->orderBy('depth', 'ASC')
526
            ->orderBy('node_id', 'ASC');
527
528
        $this->output->writeln(
529
            "Publishing URL aliases for {$totalLocationCount} Location(s) " .
530
            "with {$totalContentCount} Content item(s) in all languages."
531
        );
532
533
        $progressBar = $this->getProgressBar($totalLocationCount);
534
        $progressBar->start();
535
536
        for ($pass = 0; $pass <= $passCount; ++$pass) {
537
            $rows = $this->loadLocationData($queryBuilder, $pass);
538
539
            foreach ($rows as $row) {
540
                $publishedAliasCount += $this->publishAliases(
541
                    $row['node_id'],
542
                    $row['parent_node_id'],
543
                    $row['contentobject_id']
544
                );
545
            }
546
547
            $progressBar->advance(count($rows));
548
        }
549
550
        $progressBar->finish();
551
552
        $this->output->writeln('');
553
        $this->output->writeln("Done. Published {$publishedAliasCount} URL alias(es).");
554
        $this->output->writeln('');
555
    }
556
557
    /**
558
     * Publishes URL aliases in all languages for the given parameters.
559
     *
560
     * @throws \Exception
561
     *
562
     * @param int|string $locationId
563
     * @param int|string $parentLocationId
564
     * @param int|string $contentId
565
     *
566
     * @return int
567
     */
568
    protected function publishAliases($locationId, $parentLocationId, $contentId)
569
    {
570
        $content = $this->contentService->loadContent($contentId);
571
572
        $urlAliasNames = $this->nameSchemaResolver->resolveUrlAliasSchema($content);
573
574
        foreach ($urlAliasNames as $languageCode => $name) {
575
            try {
576
                $this->setMigrationTable();
577
                $this->urlAliasHandler->publishUrlAliasForLocation(
578
                    $locationId,
579
                    $parentLocationId,
580
                    $name,
581
                    $languageCode,
582
                    $content->contentInfo->alwaysAvailable
583
                );
584
                $this->setDefaultTable();
585
            } catch (Exception $e) {
586
                $this->setDefaultTable();
587
                throw $e;
588
            }
589
        }
590
591
        return count($urlAliasNames);
592
    }
593
594
    /**
595
     * Loads Location data for the given $pass.
596
     *
597
     * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder
598
     * @param int $pass
599
     *
600
     * @return array
601
     */
602 View Code Duplication
    protected function loadLocationData(QueryBuilder $queryBuilder, $pass)
603
    {
604
        $queryBuilder->setFirstResult($pass * $this->bulkCount);
605
        $queryBuilder->setMaxResults($this->bulkCount);
606
607
        $statement = $queryBuilder->execute();
608
609
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
610
611
        return $rows;
612
    }
613
614
    /**
615
     * Returns total number of Locations in the database.
616
     *
617
     * The number excludes absolute root Location, which does not have an URL alias.
618
     */
619 View Code Duplication
    protected function getTotalLocationCount()
620
    {
621
        $platform = $this->connection->getDatabasePlatform();
622
623
        $queryBuilder = $this->connection->createQueryBuilder();
624
        $queryBuilder
625
            ->select($platform->getCountExpression('node_id'))
626
            ->from('ezcontentobject_tree')
627
            ->where(
628
                $queryBuilder->expr()->neq(
629
                    'node_id',
630
                    UrlAliasHandler::ROOT_LOCATION_ID
631
                )
632
            );
633
634
        return $queryBuilder->execute()->fetchColumn();
635
    }
636
637
    /**
638
     * Returns total number of Content objects having a Location in the database.
639
     *
640
     * The number excludes absolute root Location, which does not have an URL alias.
641
     */
642 View Code Duplication
    protected function getTotalLocationContentCount()
643
    {
644
        $platform = $this->connection->getDatabasePlatform();
645
646
        $queryBuilder = $this->connection->createQueryBuilder();
647
        $queryBuilder
648
            ->select($platform->getCountExpression('DISTINCT contentobject_id'))
649
            ->from('ezcontentobject_tree')
650
            ->where(
651
                $queryBuilder->expr()->neq(
652
                    'node_id',
653
                    UrlAliasHandler::ROOT_LOCATION_ID
654
                )
655
            );
656
657
        return $queryBuilder->execute()->fetchColumn();
658
    }
659
660
    /**
661
     * Returns total number of Content objects having a Location in the database.
662
     *
663
     * The number excludes absolute root Location, which does not have an URL alias.
664
     */
665 View Code Duplication
    protected function getTotalCustomUrlAliasBackupCount()
666
    {
667
        $platform = $this->connection->getDatabasePlatform();
668
669
        $queryBuilder = $this->connection->createQueryBuilder();
670
        $queryBuilder
671
            ->select($platform->getCountExpression('id'))
672
            ->from(static::CUSTOM_ALIAS_BACKUP_TABLE);
673
674
        return $queryBuilder->execute()->fetchColumn();
675
    }
676
677
    /**
678
     * Creates database table for custom URL alias backup.
679
     */
680
    protected function createCustomAliasBackupTable()
681
    {
682
        $schema = new Schema();
683
684
        $table = $schema->createTable(static::CUSTOM_ALIAS_BACKUP_TABLE);
685
686
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
687
        $table->addColumn('location_id', 'integer');
688
        $table->addColumn('path', 'text');
689
        $table->addColumn('language_code', 'string');
690
        $table->addColumn('always_available', 'integer');
691
        $table->addColumn('forwarding', 'integer');
692
        $table->setPrimaryKey(['id']);
693
694
        $queries = $schema->toSql($this->connection->getDatabasePlatform());
695
696
        foreach ($queries as $query) {
697
            $this->connection->exec($query);
698
        }
699
    }
700
701
    /**
702
     * Checks if database table $name exists.
703
     *
704
     * @param string $name
705
     *
706
     * @return bool
707
     */
708
    protected function tableExists($name)
709
    {
710
        return $this->connection->getSchemaManager()->tablesExist([$name]);
711
    }
712
713
    /**
714
     * Checks if database table $name is empty.
715
     *
716
     * @param string $name
717
     *
718
     * @return bool
719
     */
720 View Code Duplication
    protected function isTableEmpty($name)
721
    {
722
        $queryBuilder = $this->connection->createQueryBuilder();
723
        $queryBuilder
724
            ->select($this->connection->getDatabasePlatform()->getCountExpression('*'))
725
            ->from($name);
726
727
        $count = $queryBuilder->execute()->fetchColumn();
728
729
        return $count == 0;
730
    }
731
732
    /**
733
     * Returns configured progress bar helper.
734
     *
735
     * @param int $maxSteps
736
     *
737
     * @return \Symfony\Component\Console\Helper\ProgressBar
738
     */
739
    protected function getProgressBar($maxSteps)
740
    {
741
        $progressBar = new ProgressBar($this->output, $maxSteps);
742
        $progressBar->setFormat(
743
            ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%'
744
        );
745
746
        return $progressBar;
747
    }
748
}
749