Passed
Branch master (fd6afc)
by Dāvis
03:04
created

QuickInsertRepository   F

Complexity

Total Complexity 132

Size/Duplication

Total Lines 496
Duplicated Lines 12.5 %

Importance

Changes 0
Metric Value
dl 62
loc 496
rs 1.5789
c 0
b 0
f 0
wmc 132

15 Methods

Rating   Name   Duplication   Size   Complexity  
C update() 9 72 17
B init() 0 17 5
B isEmpty() 0 13 5
C buildExtra() 0 38 8
A extract() 0 8 1
A findNextIdExt() 0 6 1
B findNextId() 0 24 3
B link() 0 26 5
F persist() 23 89 32
C get() 0 53 17
B extractExt() 0 24 4
A close() 0 5 2
A delete() 0 14 2
A getTable() 0 12 4
C buildWhere() 30 54 26

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like QuickInsertRepository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QuickInsertRepository, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Sludio\HelperBundle\Script\Repository;
4
5
class QuickInsertRepository
6
{
7
    private static $mock = [];
8
    private static $metadata = [];
9
    private static $tableName;
10
11
    public static $entityManager;
12
    public static $connection;
13
14
    public static function init($noFkCheck = false, $manager = null)
15
    {
16
        if (!self::$connection) {
17
            global $kernel;
18
19
            if ('AppCache' === get_class($kernel)) {
20
                $kernel = $kernel->getKernel();
21
            }
22
            $container = $kernel->getContainer();
23
24
            $manager = $manager ?: $container->getParameter('sludio_helper.entity.manager');
25
            self::$entityManager = $container->get('doctrine')->getManager($manager);
26
            self::$connection = self::$entityManager->getConnection();
27
28
            if (!$noFkCheck) {
29
                $sth = self::$connection->prepare('SET FOREIGN_KEY_CHECKS = 0');
30
                $sth->execute();
31
            }
32
        }
33
    }
34
35
    public static function close($noFkCheck = false)
36
    {
37
        if (!$noFkCheck) {
38
            $sth = self::$connection->prepare('SET FOREIGN_KEY_CHECKS = 1');
39
            $sth->execute();
40
        }
41
    }
42
43
    public static function isEmpty($variable)
44
    {
45
        $result = true;
46
47
        if (is_array($variable) && count($variable) > 0) {
48
            foreach ($variable as $value) {
49
                $result = $result && self::isEmpty($value);
50
            }
51
        } else {
52
            $result = empty($variable);
53
        }
54
55
        return $result;
56
    }
57
58
    private static function extract($object)
59
    {
60
        self::init(false);
61
        $data = self::extractExt($object, self::$entityManager);
62
63
        self::$mock = $data['mock'];
64
        self::$tableName = $data['table'];
65
        self::$metadata[$data['table']] = $data['meta'];
66
    }
67
68
    public static function extractExt($object, $entityManager)
69
    {
70
        $metadata = $entityManager->getClassMetadata(get_class($object));
71
72
        $fields = $metadata->getFieldNames();
73
        $columns = $metadata->getColumnNames();
74
        $table = $metadata->getTableName();
75
76
        $result = [];
77
        foreach ($fields as $key => $field) {
78
            foreach ($columns as $key2 => $column) {
79
                if ($key === $key2) {
80
                    $result[$table][$field] = $column;
81
                }
82
            }
83
        }
84
85
        $data = [
86
            'mock' => $result,
87
            'table' => $table,
88
            'meta' => $metadata,
89
        ];
90
91
        return $data;
92
    }
93
94
    private static function buildExtra($extra)
95
    {
96
        $methods = [
97
            'GROUP BY',
98
            'HAVING',
99
            'ORDER BY',
100
        ];
101
        $sql = '';
102
103
        foreach ($methods as $method) {
104
            if (isset($extra[$method])) {
105
                $sql .= ' '.$method.' ';
106
                if (is_array($extra[$method])) {
107
                    foreach ($extra[$method] as $group) {
108
                        $sql .= $group.' ';
109
                    }
110
                } else {
111
                    $sql .= $extra[$method].' ';
112
                }
113
            }
114
        }
115
116
        if (isset($extra['LIMIT'])) {
117
            if (is_array($extra['LIMIT'])) {
118
                if (isset($extra['LIMIT'][1])) {
119
                    $offset = $extra['LIMIT'][0];
120
                    $limit = $extra['LIMIT'][1];
121
                } else {
122
                    $offset = 0;
123
                    $limit = $extra['LIMIT'][0];
124
                }
125
                $sql .= 'LIMIT '.$offset.', '.$limit;
126
            }
127
        }
128
129
        $sql = str_replace('  ', ' ', $sql);
130
131
        return $sql;
132
    }
133
134
    private static function buildWhere($tableName, $where)
135
    {
136
        $whereSql = $fvalue = $fkey = '';
137
        if ($where && is_array($where)) {
138
            $skip = false;
139
            foreach ($where as $key => $value) {
140
                $fkey = $key;
141
                if (is_array($value)) {
142
                    $skip = true;
143
                    $fvalue = trim($value[0]);
144
                } else {
145
                    $fvalue = trim($value);
146
                }
147
                break;
148
            }
149 View Code Duplication
            if (!$skip && isset(self::$mock[$tableName][$fkey])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
150
                if (is_numeric($fvalue)) {
151
                    $whereSql .= ' WHERE '.self::$mock[$tableName][$fkey]." = $fvalue";
152
                } else {
153
                    $whereSql .= ' WHERE '.self::$mock[$tableName][$fkey]." = '".addslashes(trim($fvalue))."'";
154
                }
155
            } else {
156
                if (!$skip && is_numeric($fvalue)) {
157
                    $whereSql .= ' WHERE '.$fkey." = $fvalue";
158
                } elseif (!$skip && !is_numeric($fvalue)) {
159
                    $whereSql .= ' WHERE '.$fkey." = '".addslashes(trim($fvalue))."'";
160
                } elseif ($skip && is_numeric($fkey)) {
161
                    $whereSql .= " WHERE $fvalue";
162
                }
163
            }
164
            unset($where[$fkey]);
165
            if ($where && is_array($where)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $where of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
166
                foreach ($where as $key => $value) {
167
                    $skip = is_array($value);
168 View Code Duplication
                    if (!$skip && isset(self::$mock[$tableName][$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
169
                        if (is_numeric($value)) {
170
                            $whereSql .= ' AND '.self::$mock[$tableName][$key]." = $value";
171
                        } else {
172
                            $whereSql .= ' AND '.self::$mock[$tableName][$key]." = '".addslashes(trim($value))."'";
173
                        }
174
                    } else {
175
                        if (!$skip && is_numeric($value)) {
176
                            $whereSql .= ' AND '.$key." = $value";
177
                        } elseif (!$skip && !is_numeric($value)) {
178
                            $whereSql .= ' AND '.$key." = '".addslashes(trim($value))."'";
179
                        } elseif ($skip && is_numeric($key)) {
180
                            $whereSql .= " AND {$value[0]}";
181
                        }
182
                    }
183
                }
184
            }
185
        }
186
187
        return $whereSql;
188
    }
189
190
    public static function findNextId($tableName, &$out = null)
191
    {
192
        $sql = "
193
            SELECT
194
                AUTO_INCREMENT
195
            FROM
196
                information_schema.tables
197
            WHERE
198
                table_name = '".$tableName."'
199
            AND
200
                table_schema = DATABASE()
201
        ";
202
        if ($out) {
203
            $out = $sql;
204
        }
205
        $sth = self::$connection->prepare($sql);
206
        $sth->execute();
207
        $result = $sth->fetch();
208
209
        if (isset($result['AUTO_INCREMENT'])) {
210
            return (int)$result['AUTO_INCREMENT'];
211
        }
212
213
        return 1;
214
    }
215
216
    public static function findNextIdExt($object, $entityManager, &$out = null)
217
    {
218
        self::init(true);
219
        $data = self::extractExt($object, $entityManager);
220
221
        return self::findNextId($data['table'], $out);
222
    }
223
224
    public static function get($object, $one = false, $where = [], $noFkCheck = true, $fields = [], $manager = null, $extra = [], &$out = null)
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 4 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
225
    {
226
        self::init(true, $manager);
227
        self::getTable($object, $tableName, $columns, $type);
228
229
        $whereSql = self::buildWhere($tableName, $where);
230
        $select = (isset($extra['MODE']) ? 'SELECT '.$extra['MODE'] : 'SELECT').' ';
231
        if (!$fields) {
232
            $sql = $select.'id FROM '.$tableName.' '.$whereSql;
233
        } else {
234
            $sql = $select.(implode(', ', $fields)).' FROM '.$tableName.' '.$whereSql;
235
        }
236
        if (!empty($extra)) {
237
            $extraSql = self::buildExtra($extra);
238
            $sql .= $extraSql;
239
        }
240
        if ($out) {
241
            $out = $sql;
242
        }
243
        $sth = self::$connection->prepare($sql);
244
        $sth->execute();
245
        $result = $sth->fetchAll();
246
        if ($one && $result) {
247
            if (!$fields) {
248
                return intval($result[0]['id']);
249
            } else {
250
                if (count($fields) === 1 && $fields[0] !== '*') {
251
                    return $result[0][$fields[0]];
252
                } else {
253
                    return $result[0];
254
                }
255
            }
256
        }
257
258
        self::close(true);
259
        if ($one || !$result) {
260
            return null;
261
        }
262
263
        $field = null;
264
        if (!$fields) {
265
            $field = 'id';
266
        } elseif (count($fields) === 1 && $fields[0] !== '*') {
267
            $field = $fields[0];
268
        }
269
270
        if ($field) {
271
            foreach ($result as &$res) {
272
                $res = $res[$field];
273
            }
274
        }
275
276
        return $result;
277
    }
278
279
    private static function getTable(&$object, &$tableName, &$columns, &$type)
280
    {
281
        if (is_object($object)) {
282
            self::extract($object);
283
            $tableName = self::$tableName;
284
            $columns = self::$mock[$tableName] ?: [];
285
            $type = 'object';
286
        } else {
287
            $tableName = $object['table_name'];
288
            unset($object['table_name']);
289
            $type = 'table';
290
            $columns = array_keys($object) ?: [];
291
        }
292
    }
293
294
    public static function persist($object, $full = false, $extraFields = [], $noFkCheck = false, $manager = null, &$out = null)
295
    {
296
        self::init($noFkCheck, $manager);
297
        self::getTable($object, $tableName, $columns, $type);
298
299
        $id = self::findNextId($tableName);
300
        $keys = [];
301
        $values = [];
302
303 View Code Duplication
        if (!empty($extraFields) && isset($extraFields[$tableName])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
304
            $columns = array_merge($columns, $extraFields[$tableName]);
305
        }
306
307
        if ($type === 'object') {
308
            foreach ($columns as $value => $key) {
309
                $variable = null;
310
                if (!is_array($key) && !is_array($value)) {
311
                    if ($object->{'get'.ucfirst($value)}() instanceof \DateTime) {
312
                        $variable = "'".addslashes(trim($object->{'get'.ucfirst($value)}()->format('Y-m-d H:i:s')))."'";
313
                    } else {
314
                        $variable = "'".addslashes(trim($object->{'get'.ucfirst($value)}()))."'";
315
                    }
316 View Code Duplication
                    if (trim($variable) === '' || trim($variable) === "''" || (is_numeric($variable) && $variable === 0)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
317
                        $variable = null;
318
                    }
319 View Code Duplication
                    if ($variable !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
320
                        $values[] = $variable;
321
                        $keys[] = $key;
322
                        if ($key === 'id') {
323
                            $idd = $object->{'get'.ucfirst($value)}();
324
                        }
325
                    }
326
                }
327
            }
328
        } else {
329
            foreach ($columns as $value => $key) {
330
                $variable = null;
331
                if (!is_array($key) && !is_array($value) && isset($object[$value])) {
332
                    if ($object[$value] instanceof \DateTime) {
333
                        $variable = "'".addslashes(trim($object[$value]->format('Y-m-d H:i:s')))."'";
334
                    } else {
335
                        $variable = "'".addslashes(trim($object[$value]))."'";
336
                    }
337 View Code Duplication
                    if (trim($variable) === '' || trim($variable) === "''" || (is_numeric($variable) && $variable === 0)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
338
                        $variable = null;
339
                    }
340 View Code Duplication
                    if ($variable !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
341
                        $values[] = $variable;
342
                        $keys[] = $key;
343
                        if ($key === 'id') {
344
                            $idd = $object[$value];
345
                        }
346
                    }
347
                }
348
            }
349
        }
350
351
        $sql = null;
352
        if (!$full && !self::isEmpty($values)) {
353
            $sql = '
354
                INSERT INTO
355
                    '.$tableName.'
356
                        (id, '.implode(',', $keys).")
357
                VALUES
358
                    ({$id},".implode(',', $values).')
359
            ';
360
        } elseif ($full && !self::isEmpty($values)) {
361
            $id = $idd;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idd does not seem to be defined for all execution paths leading up to this point.
Loading history...
362
            $sql = '
363
                INSERT INTO
364
                    '.$tableName.'
365
                        ('.implode(',', $keys).")
366
                VALUES
367
                    (".implode(',', $values).')
368
            ';
369
        } else {
370
            $id = null;
371
        }
372
        if ($sql !== null && $id !== null) {
373
            if ($out) {
374
                $out = $sql;
375
            }
376
            $sth = self::$connection->prepare($sql);
377
            $sth->execute();
378
        }
379
380
        self::close($noFkCheck);
381
382
        return $id;
383
    }
384
385
    public static function update($id, $object, $extraFields = [], $noFkCheck = false, $manager = null, &$out = null)
386
    {
387
        self::init($noFkCheck, $manager);
388
        self::getTable($object, $tableName, $columns, $type);
389
390
        $result = self::get($tableName, true, ['id' => $id], true, ['*']);
391
        unset($result['id']);
392
393
        $data = [];
394
395 View Code Duplication
        if (!empty($extraFields) && isset($extraFields[$tableName])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
396
            $columns = array_merge($columns, $extraFields[$tableName]);
397
        }
398
399
        $flip = array_flip($columns);
400
        if ($type === 'object') {
401
            if ($id) {
402
                foreach ($result as $key => $value) {
403 View Code Duplication
                    if ($object->{'get'.ucfirst($flip[$key])}() !== $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
404
                        $data[$columns[$flip[$key]]] = $object->{'get'.ucfirst($flip[$key])}();
405
                    }
406
                }
407
            } else {
408
                foreach ($result as $key => $value) {
409
                    if ($object->{'get'.ucfirst($flip[$key])}() !== null) {
410 View Code Duplication
                        if ($object->{'get'.ucfirst($flip[$key])}() !== $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
411
                            $data[$columns[$flip[$key]]] = $object->{'get'.ucfirst($flip[$key])}();
412
                        }
413
                    }
414
                }
415
            }
416
        } else {
417
            foreach ($result as $key => $value) {
418
                if (isset($object[$key]) && $object[$key] !== $value) {
419
                    $data[$key] = $extraFields[$key];
420
                }
421
            }
422
423
        }
424
425
        if ($data) {
426
            $sql = "
427
                UPDATE
428
                    ".$tableName."
429
                SET
430
431
            ";
432
            foreach ($data as $key => $value) {
433
                $meta = self::$metadata[$tableName]->getFieldMapping($flip[$key]);
434
                $meta = $meta['type'];
435
                if (in_array($meta, [
436
                    'boolean',
437
                    'integer',
438
                    'longint',
439
                ])) {
440
                    $value = intval($value);
441
                } else {
442
                    $value = "'".addslashes(trim($value))."'";
443
                }
444
                $sql .= " ".$key." = ".$value.",";
445
            }
446
            $sql = substr($sql, 0, -1);
447
            $sql .= " WHERE id = ".$id;
448
            if ($out) {
449
                $out = $sql;
450
            }
451
452
            $sthu = self::$connection->prepare($sql);
453
            $sthu->execute();
454
        }
455
456
        self::close($noFkCheck);
457
    }
458
459
    public static function delete($object, $where = [], $noFkCheck = false, $manager = null, &$out = null)
460
    {
461
        self::init($noFkCheck, $manager);
462
        self::getTable($object, $tableName, $columns, $type);
463
464
        $whereSql = self::buildWhere($tableName, $where);
465
        $sql = 'DELETE FROM '.$tableName.' '.$whereSql;
466
        if ($out) {
467
            $out = $sql;
468
        }
469
        $sth = self::$connection->prepare($sql);
470
        $sth->execute();
471
472
        self::close($noFkCheck);
473
    }
474
475
    public static function link($object, $data, $noFkCheck = false, $manager = null, &$out = null)
476
    {
477
        self::init($noFkCheck, $manager);
478
        self::getTable($object, $tableName, $columns, $type);
479
480
        if ($object && $data) {
481
            $keys = $values = [];
482
            foreach ($data as $key => $value) {
483
                $keys[] = $key;
484
                $values[] = $value;
485
            }
486
            $sql = "
487
                INSERT IGNORE INTO
488
                    ".$tableName."
489
                        (".implode(',', $keys).")
490
                VALUES
491
                    (".implode(',', $values).")
492
            ";
493
            if ($out) {
494
                $out = $sql;
495
            }
496
            $sth = self::$connection->prepare($sql);
497
            $sth->execute();
498
        }
499
500
        self::close($noFkCheck);
501
    }
502
}
503