Passed
Pull Request — master (#224)
by David
02:58
created

ColumnsReorderer::reorderColumnsForTable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 15
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
4
namespace TheCodingMachine\TDBM\Utils;
5
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\Schema;
8
use Doctrine\DBAL\Schema\Table;
9
use ReflectionClass;
10
use function array_merge;
11
use function in_array;
12
13
/**
14
 * The sole purpose of this class is to work around a DBAL bug related to Oracle that
15
 * does not return the columns in the same order as the other databases.
16
 * In order to have consistent results (and consistent tests), we need those columns in
17
 * the same order in all databases.
18
 */
19
class ColumnsReorderer
20
{
21
    public static function reorderTableColumns(Schema $schema): void
22
    {
23
        foreach ($schema->getTables() as $table) {
24
            self::reorderColumnsForTable($table);
25
        }
26
    }
27
28
    private static function reorderColumnsForTable(Table $table)
29
    {
30
        $columns = self::getColumnsInExpectedOrder($table);
31
32
        // Note: the only way to rewrite columns order is to tap into a PROTECTED method.
33
        // This is bad BUT!
34
        // - we only do this for Oracle databases
35
        // - we know Doctrine consider protected methods as part of the API for BC changes so risk is limited.
36
        $refClass = new ReflectionClass(Table::class);
37
        $addColumnMethod = $refClass->getMethod('_addColumn');
38
        $addColumnMethod->setAccessible(true);
39
40
        foreach ($columns as $column) {
41
            $table->dropColumn($column->getName());
42
            $addColumnMethod->invoke($table, $column);
43
            //$table->_addColumn($column);
44
        }
45
    }
46
47
    /**
48
     * @param Table $table
49
     * @return Column[]
50
     */
51
    private static function getColumnsInExpectedOrder(Table $table): array {
52
        if ($table->hasPrimaryKey()) {
53
            $pkColumns = $table->getPrimaryKey()->getUnquotedColumns();
54
        } else {
55
            $pkColumns = [];
56
        }
57
        $fks = $table->getForeignKeys();
58
59
        $fkColumns = [];
60
61
        foreach ($fks as $fk) {
62
            $fkColumns = array_merge($fkColumns, $fk->getUnquotedLocalColumns());
63
        }
64
65
        $first = [];
66
        $second = [];
67
        $last = [];
68
69
        foreach ($table->getColumns() as $column) {
70
            if (in_array($column->getName(), $pkColumns, true)) {
71
                $first[] = $column;
72
            } elseif (in_array($column->getName(), $fkColumns, true)) {
73
                $second[] = $column;
74
            } else {
75
                $last[] = $column;
76
            }
77
        }
78
        return array_merge($first, $second, $last);
79
    }
80
}
81