DBALMysqlResource::getCreateTableSql()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 19
c 0
b 0
f 0
ccs 16
cts 16
cp 1
rs 9.6333
cc 4
nc 6
nop 2
crap 4
1
<?php
2
3
namespace Maketok\DataMigration\Storage\Db;
4
5
use Doctrine\DBAL\Driver as DriverInterface;
6
use Doctrine\DBAL\Driver\PDOMySql\Driver;
7
use Doctrine\DBAL\Schema\Schema;
8
use Maketok\DataMigration\Storage\Exception\ParsingException;
9
10
class DBALMysqlResource extends AbstractDBALResource
11
{
12
    /**
13
     * @var DriverInterface
14
     */
15
    private $driver;
16
17
    /**
18
     * {@inheritdoc}
19
     */
20 31
    protected function getDriverOptions()
21
    {
22
        return [
23 31
            \PDO::MYSQL_ATTR_LOCAL_INFILE => true,
24 31
            \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
25 31
        ];
26
    }
27
28
    /**
29
     * {@inheritdoc}
30
     */
31 2
    public function deleteUsingTempPK($deleteTable, $tmpTable, $primaryKey = 'id')
32
    {
33 2
        $sql = $this->getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey);
34 2
        return $this->connection->executeUpdate($sql);
35
    }
36
37
    /**
38
     * @param string $deleteTable
39
     * @param string $tmpTable
40
     * @param string|string[] $primaryKey
41
     * @return string
42
     */
43 3
    public function getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey)
44
    {
45 3
        $deleteTable = $this->connection->quoteIdentifier($deleteTable);
46 3
        $tmpTable = $this->connection->quoteIdentifier($tmpTable);
47 3
        if (!is_array($primaryKey)) {
48 2
            $primaryKey = [$primaryKey];
49 2
        }
50 3
        $primaryKey = array_map([$this->connection, 'quoteIdentifier'], $primaryKey);
51 3
        $conditionParts = [];
52 3
        foreach ($primaryKey as $key) {
53 3
            $conditionParts[] = "`main_table`.$key=`tmp_table`.$key";
54 3
        }
55 3
        $condition = implode('AND', $conditionParts);
56
        return <<<MYSQL
57
DELETE main_table FROM $deleteTable AS main_table
58 3
JOIN $tmpTable AS tmp_table ON $condition
59 3
MYSQL;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     * @param string $delimiter
65
     * @param string $enclosure
66
     * @param string $escape
67
     * @param string $termination
68
     * @param bool $optionallyEnclosed
69
     */
70 6
    public function loadData(
0 ignored issues
show
Complexity introduced by
This method has 10 parameters which exceeds the configured maximum of 10.

A high number of parameters is generally an indication that you should consider creating a dedicated object for the parameters.

Let’s take a look at an example:

<?php

class SomeClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ..
    }
}

class AnotherClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

could be refactored to:

class Context
{
    private $a;
    private $b;
    private $c;
    private $d;
    private $e;
    private $f;

    public function __construct(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

class SomeClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}

class AnotherClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}
Loading history...
71
        $table,
72
        $file,
73
        $local = false,
74
        array $columns = [],
75
        array $set = [],
76
        $delimiter = ",",
77
        $enclosure = '"',
78
        $escape = '\\',
79
        $termination = '\n',
80
        $optionallyEnclosed = true
81
    ) {
82 6
        $sql = $this->getLoadDataSql($table, $file, $local, $columns, $set, $delimiter, $enclosure,
83 6
            $escape, $termination, $optionallyEnclosed);
84 6
        return $this->connection->executeUpdate($sql);
85
    }
86
87
    /**
88
     * @param string $table
89
     * @param string $file
90
     * @param bool|false $local
91
     * @param array $columns
92
     * @param array $set
93
     * @param string $delimiter
94
     * @param string $enclosure
95
     * @param string $escape
96
     * @param string $termination
97
     * @param bool|true $optionallyEnclosed
98
     * @return string
99
     */
100 7
    public function getLoadDataSql(
0 ignored issues
show
Complexity introduced by
This method has 10 parameters which exceeds the configured maximum of 10.

A high number of parameters is generally an indication that you should consider creating a dedicated object for the parameters.

Let’s take a look at an example:

<?php

class SomeClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ..
    }
}

class AnotherClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

could be refactored to:

class Context
{
    private $a;
    private $b;
    private $c;
    private $d;
    private $e;
    private $f;

    public function __construct(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

class SomeClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}

class AnotherClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}
Loading history...
101
        $table,
102
        $file,
103
        $local = false,
104
        array $columns = [],
105
        array $set = [],
106
        $delimiter = ",",
107
        $enclosure = '"',
108
        $escape = "\\",
109
        $termination = '\n',
110
        $optionallyEnclosed = true
111
    ) {
112 7
        $localKey = $local ? 'LOCAL' : '';
113 7
        $table = $this->connection->quoteIdentifier($table);
114 7
        $optionalKey = $optionallyEnclosed ? 'OPTIONALLY' : '';
115 7
        if (!empty($columns)) {
116 1
            $columns = '(' . implode(',', $columns) . ')';
117 1
        } else {
118 6
            $columns = '';
119
        }
120 7
        if (!empty($set)) {
121 1
            $setParts = [];
122 1
            foreach ($set as $key => $val) {
123 1
                $setParts[] = "$key=$val";
124 1
            }
125 1
            $set = 'SET ' . implode(',', $setParts);
126 1
        } else {
127 6
            $set = '';
128
        }
129 7
        $escape = $this->connection->quote($escape);
130
        return <<<MYSQL
131 7
LOAD DATA $localKey INFILE '$file'
132 7
INTO TABLE $table
133
CHARACTER SET UTF8
134
FIELDS
135 7
    TERMINATED BY '$delimiter'
136 7
    $optionalKey ENCLOSED BY '$enclosure'
137 7
    ESCAPED BY $escape
138
LINES
139 7
    TERMINATED BY '$termination'
140 7
$columns
141 7
$set
142 7
MYSQL;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 9
    public function move(
149
        $fromTable,
150
        $toTable,
151
        array $columns = [],
152
        array $conditions = [],
153
        array $orderBy = [],
154
        $dir = 'ASC'
155
    ) {
156 9
        $sql = $this->getMoveSql($fromTable, $toTable, $columns, $conditions, $orderBy, $dir);
157 9
        return $this->connection->executeUpdate($sql);
158
    }
159
160
    /**
161
     * @param string $fromTable
162
     * @param string $toTable
163
     * @param array $columns
164
     * @param array $conditions
165
     * @param array $orderBy
166
     * @param string $dir
167
     * @return string
168
     */
169 18
    public function getMoveSql(
170
        $fromTable,
171
        $toTable,
172
        array $columns = [],
173
        array $conditions = [],
174
        array $orderBy = [],
175
        $dir = 'ASC'
176
    ) {
177 18
        $selectColumns = '*';
178 18
        $onDuplicate = '';
179 18
        $fromTable = $this->connection->quoteIdentifier($fromTable);
180 18
        $toTable = $this->connection->quoteIdentifier($toTable);
181 18
        if (!empty($columns)) {
182 18
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
183 18
            $selectColumns = implode(',', $columns);
184 18
            $duplicateParts = array_map(function ($var) {
185 18
                return "$var=VALUES($var)";
186 18
            }, $columns);
187 18
            $columns = '(' . $selectColumns . ')';
188 18
            $onDuplicate = 'ON DUPLICATE KEY UPDATE ' . implode(',', $duplicateParts);
189 18
        } else {
190
            $columns = '';
191
        }
192 18
        if (!empty($conditions)) {
193 10
            $conditionParts = [];
194 10
            foreach ($conditions as $key => $val) {
195 10
                $key = $this->connection->quoteIdentifier($key);
196 10
                if (is_array($val)) {
197 9
                    $conditionParts[] = $this->getParsedCondition("$fromTable.$key", $val);
198 5
                } else {
199 1
                    $val = $this->connection->quote($val);
200 1
                    $conditionParts[] = "$fromTable.$key=$val";
201
                }
202 6
            }
203 6
            $conditions = 'WHERE ' . implode('AND', $conditionParts);
204 6
        } else {
205 8
            $conditions = '';
206
        }
207 14
        if (!empty($orderBy)) {
208 8
            $orderBy = array_map([$this->connection, 'quoteIdentifier'], $orderBy);
209 8
            $orderBy = implode(',', $orderBy);
210 8
        } else {
211 6
            $orderBy = 'NULL';
212
        }
213
        return <<<MYSQL
214 14
INSERT INTO $toTable $columns
215 14
SELECT $selectColumns FROM $fromTable
216 14
$conditions
217 14
ORDER BY $orderBy $dir
218 14
$onDuplicate
219 14
MYSQL;
220
    }
221
222
    /**
223
     * Return valid condition
224
     * @param $column
225
     * @param array $value
226
     * @throws ParsingException
227
     * @return string
228
     */
229 9
    protected function getParsedCondition($column, array $value)
230
    {
231 9
        if (count($value) > 1) {
232 1
            throw new ParsingException("Condition should contain only 1 element");
233
        }
234 8
        $operation = key($value);
235 8
        $actualValue = current($value);
236
        switch ($operation) {
237 8
            case 'neq':
238 1
                $string = "$column<>{$this->connection->quote($actualValue)}";
239 1
                break;
240 7
            case 'eq':
241 1
                $string = "$column={$this->connection->quote($actualValue)}";
242 1
                break;
243 6
            case 'in':
244 2
                if (!is_array($actualValue)) {
245 1
                    throw new ParsingException("Can not use 'in' operation with non array.");
246
                }
247 1
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
248 1
                $glued = implode(',', $actualValue);
249 1
                $string = "$column in ($glued)";
250 1
                break;
251 4
            case 'nin':
252 3
                if (!is_array($actualValue)) {
253 1
                    throw new ParsingException("Can not use 'nin' operation with non array.");
254
                }
255 2
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
256 2
                $glued = implode(',', $actualValue);
257 2
                $string = "$column not in ($glued)";
258 2
                break;
259 1
            default:
260 1
                throw new ParsingException(sprintf("Could not resolve condition operation %s.", $operation));
261 1
        }
262 5
        return $string;
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     */
268 3
    public function dumpData($table, array $columns = [], $limit = 1000, $offset = 0)
269
    {
270 3
        $sql = $this->getDumpDataSql($table, $columns);
271 3
        $stmt = $this->connection->prepare($sql);
272 3
        $stmt->bindValue(1, $limit, \PDO::PARAM_INT);
273 3
        $stmt->bindValue(2, $offset, \PDO::PARAM_INT);
274 3
        $res = $stmt->execute();
275 3
        if ($res === false) {
276
            return false;
277
        }
278 3
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
279
    }
280
281
    /**
282
     * @param string $table
283
     * @param array $columns
284
     * @return string
285
     */
286 4
    public function getDumpDataSql($table, array $columns = [])
287
    {
288 4
        $table = $this->connection->quoteIdentifier($table);
289 4
        if (!empty($columns)) {
290 4
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
291 4
            $columns = implode(',', $columns);
292 4
        } else {
293 1
            $columns = '*';
294
        }
295
        return <<<MYSQL
296 4
SELECT $columns FROM $table
297 4
LIMIT ? OFFSET ?
298 4
MYSQL;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 11
    public function createTmpTable($name, array $columns)
305
    {
306 11
        $sql = $this->getCreateTableSql($name, $columns);
307 11
        foreach ($sql as $directive) {
308 11
            $this->connection->executeUpdate($directive);
309 11
        }
310 11
        return true;
311
    }
312
313
    /**
314
     * @param string $name
315
     * @param array $columns
316
     * @return array
317
     * @throws \Doctrine\DBAL\DBALException
318
     */
319 12
    public function getCreateTableSql($name, array $columns)
320
    {
321 12
        $schema = new Schema();
322 12
        $table = $schema->createTable($name);
323 12
        foreach ($columns as $column => $definition) {
324 12
            if (is_array($definition)) {
325 8
                $type = array_shift($definition);
326 8
                $options = array_shift($definition);
327 8
            } else {
328 5
                $type = $definition;
329 5
                $options = [];
330
            }
331 12
            $table->addColumn($column, $type, $options);
332 12
        }
333 12
        if (!$this->config['db_debug']) {
334 12
            $table->addOption('temporary', true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
335 12
        }
336 12
        return $this->connection->getDatabasePlatform()->getCreateTableSQL($table);
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342 31
    protected function getDriver()
343
    {
344 31
        if (is_null($this->driver)) {
345 31
            $driver = $this->config['db_driver'];
346 31
            if ($driver && $driver instanceof DriverInterface) {
347 13
                $this->driver = $driver;
348 13
            } else {
349 18
                $this->driver = new Driver();
350
            }
351 31
        }
352 31
        return $this->driver;
353
    }
354
}
355