Completed
Push — master ( 98e6e0...ae8169 )
by Vuong
01:24
created

DatabaseCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 158
Duplicated Lines 7.59 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 6.25%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 6
dl 12
loc 158
ccs 4
cts 64
cp 0.0625
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 6 1
A migrate() 0 21 2
A migrateDatabase() 0 20 3
A normalizeTableColumns() 12 12 2
A migrateTable() 0 32 5
A getPrimaryKey() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @link https://github.com/vuongxuongminh/migrate-phone-number
4
 * @copyright Copyright (c) 2018 Vuong Xuong Minh
5
 * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
6
 */
7
8
namespace VXM\MPN;
9
10
use Doctrine\DBAL\Connection;
11
use Doctrine\DBAL\DriverManager;
12
13
use Symfony\Component\Console\Helper\ProgressBar;
14
use Symfony\Component\Console\Question\Question;
15
16
17
/**
18
 * Lớp DatabaseCommand hổ trợ chuyển đổi dữ liệu các cột trên bảng CSDL chứa số điện thoại 11 số sang 10 số.
19
 *
20
 * @author Vuong Minh <[email protected]>
21
 * @since 1.0
22
 */
23
class DatabaseCommand extends MigrateCommand
24
{
25
26
    /**
27
     * @inheritdoc
28
     */
29
    protected static $defaultName = 'migrate:db';
30
31
    /**
32
     * @var null|Connection Đối tượng kết nối CSDL để thực thi chuyển đổi.
33
     */
34
    protected $db;
35
36
    /**
37
     * @inheritdoc
38
     */
39 3
    protected function configure(): void
40
    {
41 3
        parent::configure();
42
43 3
        $this->setDescription('Lệnh hổ trợ chuyển đổi dữ liệu các cột trên bảng CSDL chứa số điện thoại 11 số sang 10 số.');
44 3
    }
45
46
    /**
47
     * @inheritdoc
48
     * @throws \Doctrine\DBAL\ConnectionException
49
     * @throws \Doctrine\DBAL\DBALException
50
     * @throws \Throwable
51
     */
52
    protected function migrate(): void
53
    {
54
        $this->outputted->writeln('<info>Thông tin CSDL</info>');
55
56
        /** @var \Symfony\Component\Console\Helper\QuestionHelper $question */
57
        $question = $this->getHelper('question');
58
        $connection = DriverManager::getConnection([
59
            'driver' => 'pdo_' . $question->ask($this->inputted, $this->outputted, new Question('PDO Driver (mysql, pgsql, sqlsrv, sqlite): ')),
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
60
            'host' => $question->ask($this->inputted, $this->outputted, new Question('Host (ví dụ: 127.0.0.1, localhost...): ')),
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
61
            'dbname' => $question->ask($this->inputted, $this->outputted, new Question('DBName: ')),
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
62
            'user' => $question->ask($this->inputted, $this->outputted, new Question('User: ')),
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
63
            'password' => $question->ask($this->inputted, $this->outputted, (new Question('Pass: '))->setHidden(true)->setHiddenFallback(false)),
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
64
        ]);
65
66
        if ($connection->connect()) {
67
            $this->outputted->writeln('<info>Kết nối đến CSDL thành công!</info>');
68
            $this->db = $connection;
69
            $this->migrateDatabase();
70
            $this->outputted->writeln('<info>Hoàn tất!</info>');
71
        }
72
    }
73
74
    /**
75
     * Phương thức thực hiện chuyển đổi số điện thoại 11 sang 10 số.
76
     *
77
     * @throws \Doctrine\DBAL\ConnectionException
78
     * @throws \Throwable
79
     */
80
    protected function migrateDatabase(): void
81
    {
82
        /** @var \Symfony\Component\Console\Helper\QuestionHelper $question */
83
        $question = $this->getHelper('question');
84
        $tableColumns = $question->ask($this->inputted, $this->outputted, new Question('Danh sách bảng và cột (ví dụ: table1:column1, table2:column2, ...): '));
0 ignored issues
show
Bug introduced by
It seems like $this->inputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $this->outputted can be null; however, ask() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
85
86
        $this->db->beginTransaction();
87
        try {
88
            foreach ($this->normalizeTableColumns($tableColumns) as $table => $columns) {
89
                $this->migrateTable($table, $columns);
90
            }
91
92
            $this->db->commit();
93
        } catch (\Throwable $throwable) {
94
            $this->outputted->writeln("<error>Có lỗi xảy ra! Thực hiện rollback dữ liệu đã thay đổi...</error>");
95
            $this->db->rollBack();
96
97
            throw $throwable;
98
        }
99
    }
100
101
    /**
102
     * Phương thức hổ trợ chuyển đổi cấu trúc bảng cột sang mảng PHP.
103
     *
104
     * @param string $tableColumns Chuỗi bảng và cột do end-user nhập.
105
     * @return array Mảng gồm có các khóa là tên bảng và giá trị là mảng danh sách cột cần chuyển đổi số điện thoại.
106
     */
107 View Code Duplication
    protected function normalizeTableColumns(string $tableColumns): 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...
108
    {
109
        $result = [];
110
        $tableColumns = array_map('trim', explode(',', $tableColumns));
111
112
        foreach ($tableColumns as $tableColumn) {
113
            list($table, $column) = explode(':', $tableColumn);
114
            $result[$table][] = $column;
115
        }
116
117
        return $result;
118
    }
119
120
    /**
121
     * Phương thức thực hiện chuyển đổi giá trị các cột chứa số điện thoại 11 số sang 10 số theo bảng được chỉ định.
122
     *
123
     * @param string $table Bảng chỉ định.
124
     * @param array $columns Các cột chứa số điện thoại 11 số.
125
     * @throws \Doctrine\DBAL\DBALException
126
     */
127
    protected function migrateTable(string $table, array $columns): void
128
    {
129
        $primaryKey = $this->getPrimaryKey($table);
130
        $selectColumns = array_unique(array_merge($columns, $primaryKey));
131
        $queryStatement = $this->db->createQueryBuilder()->select($selectColumns)->from($table)->execute();
132
133
        if (($rowCount = $queryStatement->rowCount()) > 0) {
134
            $this->outputted->writeln("<info>Thực thi chuyển đổi dữ liệu trên bảng: `$table`...</info>");
135
            $progressBar = new ProgressBar($this->outputted, $rowCount);
0 ignored issues
show
Bug introduced by
It seems like $this->outputted can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
136
            $progressBar->setFormat('[%bar%] %percent:3s%% (%elapsed:6s%)');
137
            $progressBar->start();
138
139
            while ($row = $queryStatement->fetch()) {
140
                $updateStatement = $this->db->createQueryBuilder()->update($table);
141
                foreach ($row as $column => $value) {
142
                    if (in_array($column, $columns, true)) {
143
                        $updateStatement->set($column, ":$column")->setParameter(":$column", $this->convert($value));
144
                    }
145
146
                    $updateStatement->andWhere("$column=:{$column}_condition")->setParameter(":{$column}_condition", $value);
147
                }
148
149
                $updateStatement->execute();
150
                $progressBar->advance();
151
            }
152
153
            $progressBar->finish();
154
            $this->outputted->writeln("\n<info>Hoàn tất chuyển đổi dữ liệu trên bảng: `$table`</info>");
155
        } else {
156
            $this->outputted->writeln("<comment>Bỏ qua bảng: `$table` vì bảng rỗng.</comment>");
157
        }
158
    }
159
160
    /**
161
     * Phương thức hổ trợ lấy danh sách khóa chính trên bảng.
162
     *
163
     * @param string $table Bảng cần lấy danh sách khóa chính.
164
     *
165
     * @return array Mảng chứa danh sách khóa chính.
166
     * @throws \Doctrine\DBAL\DBALException
167
     */
168
    protected function getPrimaryKey(string $table): array
169
    {
170
        $tableDetail = $this->db->getSchemaManager()->listTableDetails($table);
171
172
        if ($tableDetail->hasPrimaryKey()) {
173
            return $tableDetail->getPrimaryKeyColumns();
174
        } else {
175
            return [];
176
        }
177
    }
178
179
180
}
181