Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database\Schema\Grammars;
4
 
5
use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
6
use Doctrine\DBAL\Schema\Comparator;
7
use Doctrine\DBAL\Schema\Table;
8
use Doctrine\DBAL\Types\Type;
9
use Illuminate\Database\Connection;
10
use Illuminate\Database\Schema\Blueprint;
11
use Illuminate\Support\Fluent;
12
use RuntimeException;
13
 
14
class ChangeColumn
15
{
16
    /**
17
     * Compile a change column command into a series of SQL statements.
18
     *
19
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
20
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
21
     * @param  \Illuminate\Support\Fluent  $command
22
     * @param  \Illuminate\Database\Connection  $connection
23
     * @return array
24
     *
25
     * @throws \RuntimeException
26
     */
27
    public static function compile($grammar, Blueprint $blueprint, Fluent $command, Connection $connection)
28
    {
29
        if (! $connection->isDoctrineAvailable()) {
30
            throw new RuntimeException(sprintf(
31
                'Changing columns for table "%s" requires Doctrine DBAL. Please install the doctrine/dbal package.',
32
                $blueprint->getTable()
33
            ));
34
        }
35
 
36
        $schema = $connection->getDoctrineSchemaManager();
37
        $databasePlatform = $schema->getDatabasePlatform();
38
        $databasePlatform->registerDoctrineTypeMapping('enum', 'string');
39
 
40
        $tableDiff = static::getChangedDiff(
41
            $grammar, $blueprint, $schema
42
        );
43
 
44
        if ($tableDiff !== false) {
45
            return (array) $databasePlatform->getAlterTableSQL($tableDiff);
46
        }
47
 
48
        return [];
49
    }
50
 
51
    /**
52
     * Get the Doctrine table difference for the given changes.
53
     *
54
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
55
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
56
     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
57
     * @return \Doctrine\DBAL\Schema\TableDiff|bool
58
     */
59
    protected static function getChangedDiff($grammar, Blueprint $blueprint, SchemaManager $schema)
60
    {
61
        $current = $schema->listTableDetails($grammar->getTablePrefix().$blueprint->getTable());
62
 
63
        return (new Comparator)->diffTable(
64
            $current, static::getTableWithColumnChanges($blueprint, $current)
65
        );
66
    }
67
 
68
    /**
69
     * Get a copy of the given Doctrine table after making the column changes.
70
     *
71
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
72
     * @param  \Doctrine\DBAL\Schema\Table  $table
73
     * @return \Doctrine\DBAL\Schema\Table
74
     */
75
    protected static function getTableWithColumnChanges(Blueprint $blueprint, Table $table)
76
    {
77
        $table = clone $table;
78
 
79
        foreach ($blueprint->getChangedColumns() as $fluent) {
80
            $column = static::getDoctrineColumn($table, $fluent);
81
 
82
            // Here we will spin through each fluent column definition and map it to the proper
83
            // Doctrine column definitions - which is necessary because Laravel and Doctrine
84
            // use some different terminology for various column attributes on the tables.
85
            foreach ($fluent->getAttributes() as $key => $value) {
86
                if (! is_null($option = static::mapFluentOptionToDoctrine($key))) {
87
                    if (method_exists($column, $method = 'set'.ucfirst($option))) {
88
                        $column->{$method}(static::mapFluentValueToDoctrine($option, $value));
89
                        continue;
90
                    }
91
 
92
                    $column->setCustomSchemaOption($option, static::mapFluentValueToDoctrine($option, $value));
93
                }
94
            }
95
        }
96
 
97
        return $table;
98
    }
99
 
100
    /**
101
     * Get the Doctrine column instance for a column change.
102
     *
103
     * @param  \Doctrine\DBAL\Schema\Table  $table
104
     * @param  \Illuminate\Support\Fluent  $fluent
105
     * @return \Doctrine\DBAL\Schema\Column
106
     */
107
    protected static function getDoctrineColumn(Table $table, Fluent $fluent)
108
    {
109
        return $table->changeColumn(
110
            $fluent['name'], static::getDoctrineColumnChangeOptions($fluent)
111
        )->getColumn($fluent['name']);
112
    }
113
 
114
    /**
115
     * Get the Doctrine column change options.
116
     *
117
     * @param  \Illuminate\Support\Fluent  $fluent
118
     * @return array
119
     */
120
    protected static function getDoctrineColumnChangeOptions(Fluent $fluent)
121
    {
122
        $options = ['type' => static::getDoctrineColumnType($fluent['type'])];
123
 
124
        if (in_array($fluent['type'], ['tinyText', 'text', 'mediumText', 'longText'])) {
125
            $options['length'] = static::calculateDoctrineTextLength($fluent['type']);
126
        }
127
 
128
        if ($fluent['type'] === 'char') {
129
            $options['fixed'] = true;
130
        }
131
 
132
        if (static::doesntNeedCharacterOptions($fluent['type'])) {
133
            $options['customSchemaOptions'] = [
134
                'collation' => '',
135
                'charset' => '',
136
            ];
137
        }
138
 
139
        return $options;
140
    }
141
 
142
    /**
143
     * Get the doctrine column type.
144
     *
145
     * @param  string  $type
146
     * @return \Doctrine\DBAL\Types\Type
147
     */
148
    protected static function getDoctrineColumnType($type)
149
    {
150
        $type = strtolower($type);
151
 
152
        return Type::getType(match ($type) {
153
            'biginteger' => 'bigint',
154
            'smallinteger' => 'smallint',
155
            'tinytext', 'mediumtext', 'longtext' => 'text',
156
            'binary' => 'blob',
157
            'uuid' => 'guid',
158
            'char' => 'string',
159
            'double' => 'float',
160
            default => $type,
161
        });
162
    }
163
 
164
    /**
165
     * Calculate the proper column length to force the Doctrine text type.
166
     *
167
     * @param  string  $type
168
     * @return int
169
     */
170
    protected static function calculateDoctrineTextLength($type)
171
    {
172
        return match ($type) {
173
            'tinyText' => 1,
174
            'mediumText' => 65535 + 1,
175
            'longText' => 16777215 + 1,
176
            default => 255 + 1,
177
        };
178
    }
179
 
180
    /**
181
     * Determine if the given type does not need character / collation options.
182
     *
183
     * @param  string  $type
184
     * @return bool
185
     */
186
    protected static function doesntNeedCharacterOptions($type)
187
    {
188
        return in_array($type, [
189
            'bigInteger',
190
            'binary',
191
            'boolean',
192
            'date',
193
            'dateTime',
194
            'decimal',
195
            'double',
196
            'float',
197
            'integer',
198
            'json',
199
            'mediumInteger',
200
            'smallInteger',
201
            'time',
202
            'timestamp',
203
            'tinyInteger',
204
        ]);
205
    }
206
 
207
    /**
208
     * Get the matching Doctrine option for a given Fluent attribute name.
209
     *
210
     * @param  string  $attribute
211
     * @return string|null
212
     */
213
    protected static function mapFluentOptionToDoctrine($attribute)
214
    {
215
        return match ($attribute) {
216
            'type', 'name' => null,
217
            'nullable' => 'notnull',
218
            'total' => 'precision',
219
            'places' => 'scale',
220
            default => $attribute,
221
        };
222
    }
223
 
224
    /**
225
     * Get the matching Doctrine value for a given Fluent attribute.
226
     *
227
     * @param  string  $option
228
     * @param  mixed  $value
229
     * @return mixed
230
     */
231
    protected static function mapFluentValueToDoctrine($option, $value)
232
    {
233
        return $option === 'notnull' ? ! $value : $value;
234
    }
235
}