Completed
Pull Request — master (#9)
by Sander
04:49
created

DBUtils::getPrimaryKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
ccs 0
cts 8
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * neuralyzer : Data Anonymization Library and CLI Tool
4
 *
5
 * PHP Version 7.1
6
 *
7
 * @author    Emmanuel Dyan
8
 * @author    Rémi Sauvat
9
 * @copyright 2018 Emmanuel Dyan
10
 *
11
 * @package   edyan/neuralyzer
12
 *
13
 * @license   GNU General Public License v2.0
14
 *
15
 * @link      https://github.com/edyan/neuralyzer
16
 */
17
18
namespace Edyan\Neuralyzer\Utils;
19
20
use Doctrine\DBAL\Connection;
21
use Doctrine\DBAL\Query\QueryBuilder;
22
use Edyan\Neuralyzer\Exception\NeuralizerException;
23
24
/**
25
 * A few generic methods to help interacting with DB
26
 */
27
class DBUtils
28
{
29
    /**
30
     * Doctrine DBAL Connection
31
     *
32
     * @var Connection
33
     */
34
    private $conn;
35
36
    /**
37
     * Set the connection (dependency)
38
     *
39
     * @param Connection $conn
40
     */
41
    public function __construct(Connection $conn)
42
    {
43
        $this->conn = $conn;
44
    }
45
46
47
    /**
48
     * Do a simple count for a table
49
     *
50
     * @param  string $table
51
     *
52
     * @return int
53
     */
54
    public function countResults(string $table): int
55
    {
56
        $queryBuilder = $this->conn->createQueryBuilder();
57
        $rows = $queryBuilder->select('COUNT(1)')->from($table)->execute();
58
59
        return (int) $rows->fetch(\Doctrine\DBAL\FetchMode::NUMERIC)[0];
60
    }
61
62
63
    /**
64
     * Identify the primary key for a table
65
     *
66
     * @param string $table
67
     *
68
     * @return string
69
     * @throws NeuralizerException
70
     */
71
    public function getPrimaryKey(string $table): string
72
    {
73
        $schema = $this->conn->getSchemaManager();
74
        $tableDetails = $schema->listTableDetails($table);
75
        if ($tableDetails->hasPrimaryKey() === false) {
76
            throw new NeuralizerException("Can't find a primary key for '{$table}'");
77
        }
78
79
        return $tableDetails->getPrimaryKey()->getColumns()[0];
80
    }
81
82
83
    /**
84
     * Retrieve columns list for a table with type and length
85
     *
86
     * @param  string $table
87
     *
88
     * @return array $cols
89
     */
90
    public function getTableCols(string $table): array
91
    {
92
        $schema = $this->conn->getSchemaManager();
93
        $tableCols = $schema->listTableColumns($table);
94
        $cols = [];
95
        foreach ($tableCols as $col) {
96
            $cols[$col->getName()] = [
97
                'length' => $col->getLength(),
98
                'type' => $col->getType(),
99
                'unsigned' => $col->getUnsigned(),
100
            ];
101
        }
102
103
        return $cols;
104
    }
105
106
107
    /**
108
     * To debug, build the final SQL (can be approximative)
109
     *
110
     * @param  QueryBuilder $queryBuilder
111
     *
112
     * @return string
113
     */
114
    public function getRawSQL(QueryBuilder $queryBuilder)
115
    {
116
        $sql = $queryBuilder->getSQL();
117
        foreach ($queryBuilder->getParameters() as $parameter => $value) {
118
            $sql = str_replace($parameter, "'$value'", $sql);
119
        }
120
121
        return $sql;
122
    }
123
124
    /**
125
     * Make sure a table exists
126
     *
127
     * @param  string $table [description]
128
     */
129
    public function assertTableExists(string $table): void
130
    {
131
        if ($this->conn->getSchemaManager()->tablesExist([$table]) === false) {
132
            throw new NeuralizerException("Table $table does not exist");
133
        }
134
    }
135
136
137
    /**
138
     * Build the condition by casting the value if needed
139
     *
140
     * @param  string $field
141
     * @param  array  $fieldConf Various values about the field
142
     *
143
     * @return string
144
     */
145
    public function getCondition(string $field, array $fieldConf): string
146
    {
147
        $type = strtolower($fieldConf['type']);
148
        $unsigned = $fieldConf['unsigned'];
149
150
        $integerCast = $this->getIntegerCast($unsigned);
151
152
        $condition = "(CASE $field WHEN NULL THEN NULL ELSE :$field END)";
153
154
        $typeToCast = [
155
            'date' => 'DATE',
156
            'datetime' => 'DATE',
157
            'time' => 'TIME',
158
            'smallint' => $integerCast,
159
            'integer' => $integerCast,
160
            'bigint' => $integerCast,
161
            'float' => 'DECIMAL',
162
            'decimal' => 'DECIMAL',
163
        ];
164
165
        // No cast required
166
        if (!array_key_exists($type, $typeToCast)) {
167
            return $condition;
168
        }
169
170
        return "CAST($condition AS {$typeToCast[$type]})";
171
    }
172
173
174
    /**
175
     * Gives an empty value according to the field (example : numeric = 0)
176
     *
177
     * @param  string $type
178
     *
179
     * @return mixed
180
     */
181
    public function getEmptyValue(string $type)
182
    {
183
        $type = strtolower($type);
184
        $typeToValue = [
185
            'date' => '1970-01-01',
186
            'datetime' => '1970-01-01 00:00:00',
187
            'time' => '00:00:00',
188
            'smallint' => 0,
189
            'integer' => 0,
190
            'bigint' => 0,
191
            'float' => 0,
192
            'decimal' => 0,
193
        ];
194
195
        // Value is simply an empty string
196
        if (!array_key_exists($type, $typeToValue)) {
197
            return '';
198
        }
199
200
        return $typeToValue[$type];
201
    }
202
203
204
    /**
205
     * Get the right CAST for an INTEGER
206
     *
207
     * @param  bool $unsigned
208
     *
209
     * @return string
210
     */
211
    private function getIntegerCast(bool $unsigned): string
212
    {
213
        $driver = $this->conn->getDriver();
214
        if (strpos($driver->getName(), 'mysql')) {
215
            return $unsigned === true ? 'UNSIGNED' : 'SIGNED';
216
        }
217
218
        return 'INTEGER';
219
    }
220
}
221