Issues (269)

src/SchemaVersionControl/SchemaBuilder.php (2 issues)

1
<?php
2
3
namespace TheCodingMachine\TDBM\SchemaVersionControl;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Doctrine\DBAL\Schema\Schema;
7
use Doctrine\DBAL\Schema\SchemaConfig;
8
use Doctrine\DBAL\Schema\Table;
9
10
/**
11
 * Database schema builder.
12
 *
13
 * Given a deep associative array supposed to describe a database schema, SchemaBuilder::build will construct a
14
 * corresponding Schema object.
15
 */
16
class SchemaBuilder
17
{
18
    /** @var array */
19
    protected $schemaDesc;
20
21
    /**
22
     * Build an array descriptor into a Schema object
23
     * @param array $schemaDesc
24
     * @return Schema
25
     */
26
    public function build(array $schemaDesc): Schema
27
    {
28
        $this->schemaDesc = $schemaDesc;
29
        $schemaConfig = new SchemaConfig();
30
        $schemaConfig->setDefaultTableOptions($schemaDesc['default_table_options'] ?? []);
31
        $schema = new Schema([], [], $schemaConfig);
32
        foreach ($schemaDesc['tables'] as $name => $tableDesc) {
33
            $table = $schema->createTable($name);
34
            $this->buildTable($tableDesc, $table);
35
        }
36
        return $schema;
37
    }
38
39
    protected function buildTable(array $tableDesc, Table $table)
40
    {
41
        $pk_columns = [];
42
43
        if (isset($tableDesc['comment'])) {
44
            $table->addOption("comment", $tableDesc['comment']);
45
        }
46
47
        foreach ($tableDesc['columns'] as $columnName => $columnDesc) {
48
            if (!is_array($columnDesc)) {
49
                $columnDesc = ['type' => $columnDesc];
50
            }
51
            if (isset($columnDesc['primary_key'])
52
                && $columnDesc['primary_key']) {
53
                $pk_columns[] = $columnName;
54
            }
55
            $column = $table->addColumn($columnName, $columnDesc['type']);
56
            $this->buildColumn($columnDesc, $column);
57
        }
58
        if (isset($tableDesc['indexes'])) {
59
            foreach ($tableDesc['indexes'] as $indexName => $indexDesc) {
60
                $this->buildIndex($indexDesc, $table, $indexName);
61
            }
62
        }
63
        if (!empty($pk_columns)) {
64
            $table->setPrimaryKey($pk_columns);
65
        }
66
        if (isset($tableDesc['foreign_keys'])) {
67
            foreach ($tableDesc['foreign_keys'] as $constraintName => $constraintDesc) {
68
                $this->buildForeignKeyConstraint($constraintDesc, $table, $constraintName);
69
            }
70
        }
71
    }
72
73
    protected function buildColumn(array $columnDesc, Column $column)
74
    {
75
        if (isset($columnDesc['fixed'])) {
76
            $column->setFixed($columnDesc['fixed']);
77
        }
78
        if (isset($columnDesc['length'])) {
79
            $column->setLength($columnDesc['length']);
80
        }
81
        if (isset($columnDesc['precision'])) {
82
            $column->setPrecision($columnDesc['precision']);
83
        }
84
        if (isset($columnDesc['scale'])) {
85
            $column->setScale($columnDesc['scale']);
86
        }
87
        $column->setNotnull(isset($columnDesc['not_null']) && $columnDesc['not_null']);
88
        if (isset($columnDesc['default'])) {
89
            $column->setDefault($columnDesc['default']);
90
        }
91
        if (isset($columnDesc['auto_increment'])) {
92
            $column->setAutoincrement($columnDesc['auto_increment']);
93
        }
94
        if (isset($columnDesc['comment'])) {
95
            $column->setComment($columnDesc['comment']);
96
        }
97
        if (isset($columnDesc['custom'])) {
98
            $column->setPlatformOptions($columnDesc['custom']);
99
        }
100
    }
101
102
    protected function buildForeignKeyConstraint(array $constraintDesc, Table $table, string $name)
103
    {
104
        if (isset($constraintDesc['column'])) {
105
            $localColumns = [$constraintDesc['column']];
106
        } else {
107
            $localColumns = $constraintDesc['columns'];
108
        }
109
        $references = $constraintDesc['references'];
110
        if (is_array($references)) {
111
            $foreignTable = $references['table'];
112
            if (isset($references['column'])) {
113
                $foreignColumns = [$references['column']];
114
            } else {
115
                $foreignColumns = $references['columns'];
116
            }
117
        } else {
118
            $foreignTable = $references;
119
            $foreignColumns = $this->getPrimaryKeyColumns($foreignTable);
120
            if (!is_array($foreignColumns)) {
0 ignored issues
show
The condition is_array($foreignColumns) is always true.
Loading history...
121
                $foreignColumns = [$foreignColumns];
122
            }
123
        }
124
        $options = array_diff_key($constraintDesc, ['column' => 0,'columns' => 0,'references' => 0]);
125
        $table->addForeignKeyConstraint($foreignTable, $localColumns, $foreignColumns, $options, $name);
126
    }
127
128
    protected function getPrimaryKeyColumns(string $tableName)
129
    {
130
        $pkColumns = [];
131
        $tableDesc = $this->schemaDesc['tables'][$tableName];
132
        foreach ($tableDesc['columns'] as $columnName => $columnDesc) {
133
            if (isset($columnDesc['primary_key'])
134
                && $columnDesc['primary_key']) {
135
                $pkColumns[] = $columnName;
136
            }
137
        }
138
        return $pkColumns;
139
    }
140
141
    protected function buildIndex($indexDesc, Table $table, $name)
142
    {
143
        if (!is_array($indexDesc)) {
144
            $indexDesc = ['column' => $indexDesc];
145
        } elseif (array_keys($indexDesc) === range(0, count($indexDesc) - 1)) {
146
            $indexDesc = ['columns' => $indexDesc];
147
        }
148
149
        if (isset($indexDesc['column'])) {
150
            $columns = [$indexDesc['column']];
151
        } else {
152
            $columns = $indexDesc['columns'];
153
        }
154
155
        if (is_int($name)) {
156
            $name = implode('_', $columns);
157
        }
158
159
        $options = array_diff_key($indexDesc, ['column' => 0,'columns' => 0]);
160
        if (isset($indexDesc['unique']) && $indexDesc['unique'] === true) {
161
            $table->addUniqueIndex($columns, $name, [], $options);
0 ignored issues
show
The call to Doctrine\DBAL\Schema\Table::addUniqueIndex() has too many arguments starting with $options. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

161
            $table->/** @scrutinizer ignore-call */ 
162
                    addUniqueIndex($columns, $name, [], $options);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
162
        } else {
163
            $table->addIndex($columns, $name, [], $options);
164
        }
165
    }
166
}
167