Completed
Push — master ( ed54f6...6ff9a3 )
by Oleg
10s
created

DBALMysqlResource::loadData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 14
nc 1
nop 10
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
        ];
25
    }
26
27
    /**
28
     * {@inheritdoc}
29
     */
30 2
    public function deleteUsingTempPK($deleteTable, $tmpTable, $primaryKey = 'id')
31
    {
32 2
        $sql = $this->getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey);
33 2
        return $this->connection->executeUpdate($sql);
34
    }
35
36
    /**
37
     * @param string $deleteTable
38
     * @param string $tmpTable
39
     * @param string|string[] $primaryKey
40
     * @return string
41
     */
42 3
    public function getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey)
43
    {
44 3
        $deleteTable = $this->connection->quoteIdentifier($deleteTable);
45 3
        $tmpTable = $this->connection->quoteIdentifier($tmpTable);
46 3
        if (!is_array($primaryKey)) {
47 2
            $primaryKey = [$primaryKey];
48
        }
49 3
        $primaryKey = array_map([$this->connection, 'quoteIdentifier'], $primaryKey);
50 3
        $conditionParts = [];
51 3
        foreach ($primaryKey as $key) {
52 3
            $conditionParts[] = "`main_table`.$key=`tmp_table`.$key";
53
        }
54 3
        $condition = implode('AND', $conditionParts);
55
        return <<<MYSQL
56 3
DELETE main_table FROM $deleteTable AS main_table
57 3
JOIN $tmpTable AS tmp_table ON $condition
58
MYSQL;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     * @param string $delimiter
64
     * @param string $enclosure
65
     * @param string $escape
66
     * @param string $termination
67
     * @param bool $optionallyEnclosed
68
     */
69 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...
70
        $table,
71
        $file,
72
        $local = false,
73
        array $columns = [],
74
        array $set = [],
75
        $delimiter = ",",
76
        $enclosure = '"',
77
        $escape = '\\',
78
        $termination = '\n',
79
        $optionallyEnclosed = true
80
    ) {
81 6
        $sql = $this->getLoadDataSql($table, $file, $local, $columns, $set, $delimiter, $enclosure,
82 6
            $escape, $termination, $optionallyEnclosed);
83 6
        return $this->connection->executeUpdate($sql);
84
    }
85
86
    /**
87
     * @param string $table
88
     * @param string $file
89
     * @param bool|false $local
90
     * @param array $columns
91
     * @param array $set
92
     * @param string $delimiter
93
     * @param string $enclosure
94
     * @param string $escape
95
     * @param string $termination
96
     * @param bool|true $optionallyEnclosed
97
     * @return string
98
     */
99 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...
100
        $table,
101
        $file,
102
        $local = false,
103
        array $columns = [],
104
        array $set = [],
105
        $delimiter = ",",
106
        $enclosure = '"',
107
        $escape = "\\",
108
        $termination = '\n',
109
        $optionallyEnclosed = true
110
    ) {
111 7
        $localKey = $local ? 'LOCAL' : '';
112 7
        $table = $this->connection->quoteIdentifier($table);
113 7
        $optionalKey = $optionallyEnclosed ? 'OPTIONALLY' : '';
114 7
        if (!empty($columns)) {
115 1
            $columns = '(' . implode(',', $columns) . ')';
116
        } else {
117 6
            $columns = '';
118
        }
119 7
        if (!empty($set)) {
120 1
            $setParts = [];
121 1
            foreach ($set as $key => $val) {
122 1
                $setParts[] = "$key=$val";
123
            }
124 1
            $set = 'SET ' . implode(',', $setParts);
125
        } else {
126 6
            $set = '';
127
        }
128 7
        $escape = $this->connection->quote($escape);
129
        return <<<MYSQL
130 7
LOAD DATA $localKey INFILE '$file'
131 7
INTO TABLE $table
132
FIELDS
133 7
    TERMINATED BY '$delimiter'
134 7
    $optionalKey ENCLOSED BY '$enclosure'
135 7
    ESCAPED BY $escape
136
LINES
137 7
    TERMINATED BY '$termination'
138 7
$columns
139 7
$set
140
MYSQL;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 9
    public function move(
147
        $fromTable,
148
        $toTable,
149
        array $columns = [],
150
        array $conditions = [],
151
        array $orderBy = [],
152
        $dir = 'ASC'
153
    ) {
154 9
        $sql = $this->getMoveSql($fromTable, $toTable, $columns, $conditions, $orderBy, $dir);
155 9
        return $this->connection->executeUpdate($sql);
156
    }
157
158
    /**
159
     * @param string $fromTable
160
     * @param string $toTable
161
     * @param array $columns
162
     * @param array $conditions
163
     * @param array $orderBy
164
     * @param string $dir
165
     * @return string
166
     */
167 18
    public function getMoveSql(
168
        $fromTable,
169
        $toTable,
170
        array $columns = [],
171
        array $conditions = [],
172
        array $orderBy = [],
173
        $dir = 'ASC'
174
    ) {
175 18
        $selectColumns = '*';
176 18
        $onDuplicate = '';
177 18
        $fromTable = $this->connection->quoteIdentifier($fromTable);
178 18
        $toTable = $this->connection->quoteIdentifier($toTable);
179 18
        if (!empty($columns)) {
180 18
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
181 18
            $selectColumns = implode(',', $columns);
182 18
            $duplicateParts = array_map(function ($var) {
183 18
                return "$var=VALUES($var)";
184 18
            }, $columns);
185 18
            $columns = '(' . $selectColumns . ')';
186 18
            $onDuplicate = 'ON DUPLICATE KEY UPDATE ' . implode(',', $duplicateParts);
187
        } else {
188
            $columns = '';
189
        }
190 18
        if (!empty($conditions)) {
191 10
            $conditionParts = [];
192 10
            foreach ($conditions as $key => $val) {
193 10
                $key = $this->connection->quoteIdentifier($key);
194 10
                if (is_array($val)) {
195 9
                    $conditionParts[] = $this->getParsedCondition("$fromTable.$key", $val);
196
                } else {
197 1
                    $val = $this->connection->quote($val);
198 6
                    $conditionParts[] = "$fromTable.$key=$val";
199
                }
200
            }
201 6
            $conditions = 'WHERE ' . implode('AND', $conditionParts);
202
        } else {
203 8
            $conditions = '';
204
        }
205 14
        if (!empty($orderBy)) {
206 8
            $orderBy = array_map([$this->connection, 'quoteIdentifier'], $orderBy);
207 8
            $orderBy = implode(',', $orderBy);
208
        } else {
209 6
            $orderBy = 'NULL';
210
        }
211
        return <<<MYSQL
212 14
INSERT INTO $toTable $columns
213 14
SELECT $selectColumns FROM $fromTable
214 14
$conditions
215 14
ORDER BY $orderBy $dir
216 14
$onDuplicate
217
MYSQL;
218
    }
219
220
    /**
221
     * Return valid condition
222
     * @param $column
223
     * @param array $value
224
     * @throws ParsingException
225
     * @return string
226
     */
227 9
    protected function getParsedCondition($column, array $value)
228
    {
229 9
        if (count($value) > 1) {
230 1
            throw new ParsingException("Condition should contain only 1 element");
231
        }
232 8
        $operation = key($value);
233 8
        $actualValue = current($value);
234
        switch ($operation) {
235 8
            case 'neq':
236 1
                $string = "$column<>{$this->connection->quote($actualValue)}";
237 1
                break;
238 7
            case 'eq':
239 1
                $string = "$column={$this->connection->quote($actualValue)}";
240 1
                break;
241 6
            case 'in':
242 2
                if (!is_array($actualValue)) {
243 1
                    throw new ParsingException("Can not use 'in' operation with non array.");
244
                }
245 1
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
246 1
                $glued = implode(',', $actualValue);
247 1
                $string = "$column in ($glued)";
248 1
                break;
249 4
            case 'nin':
250 3
                if (!is_array($actualValue)) {
251 1
                    throw new ParsingException("Can not use 'nin' operation with non array.");
252
                }
253 2
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
254 2
                $glued = implode(',', $actualValue);
255 2
                $string = "$column not in ($glued)";
256 2
                break;
257
            default:
258 1
                throw new ParsingException(sprintf("Could not resolve condition operation %s.", $operation));
259
        }
260 5
        return $string;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266 3
    public function dumpData($table, array $columns = [], $limit = 1000, $offset = 0)
267
    {
268 3
        $sql = $this->getDumpDataSql($table, $columns);
269 3
        $stmt = $this->connection->prepare($sql);
270 3
        $stmt->bindValue(1, $limit, \PDO::PARAM_INT);
271 3
        $stmt->bindValue(2, $offset, \PDO::PARAM_INT);
272 3
        $res = $stmt->execute();
273 3
        if ($res === false) {
274
            return false;
275
        }
276 3
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
277
    }
278
279
    /**
280
     * @param string $table
281
     * @param array $columns
282
     * @return string
283
     */
284 4
    public function getDumpDataSql($table, array $columns = [])
285
    {
286 4
        $table = $this->connection->quoteIdentifier($table);
287 4
        if (!empty($columns)) {
288 4
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
289 4
            $columns = implode(',', $columns);
290
        } else {
291 1
            $columns = '*';
292
        }
293
        return <<<MYSQL
294 4
SELECT $columns FROM $table
295
LIMIT ? OFFSET ?
296
MYSQL;
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302 11
    public function createTmpTable($name, array $columns)
303
    {
304 11
        $sql = $this->getCreateTableSql($name, $columns);
305 11
        foreach ($sql as $directive) {
306 11
            $this->connection->executeUpdate($directive);
307
        }
308 11
        return true;
309
    }
310
311
    /**
312
     * @param string $name
313
     * @param array $columns
314
     * @return array
315
     * @throws \Doctrine\DBAL\DBALException
316
     */
317 12
    public function getCreateTableSql($name, array $columns)
318
    {
319 12
        $schema = new Schema();
320 12
        $table = $schema->createTable($name);
321 12
        foreach ($columns as $column => $definition) {
322 12
            if (is_array($definition)) {
323 8
                $type = array_shift($definition);
324 8
                $options = array_shift($definition);
325
            } else {
326 5
                $type = $definition;
327 5
                $options = [];
328
            }
329 12
            $table->addColumn($column, $type, $options);
330
        }
331 12
        if (!$this->config['db_debug']) {
332 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...
333
        }
334 12
        return $this->connection->getDatabasePlatform()->getCreateTableSQL($table);
335
    }
336
337
    /**
338
     * {@inheritdoc}
339
     */
340 31
    protected function getDriver()
341
    {
342 31
        if (is_null($this->driver)) {
343 31
            $driver = $this->config['db_driver'];
344 31
            if ($driver && $driver instanceof DriverInterface) {
345 13
                $this->driver = $driver;
346
            } else {
347 18
                $this->driver = new Driver();
348
            }
349
        }
350 31
        return $this->driver;
351
    }
352
}
353