Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Doctrine\DBAL\Schema;
4
 
5
use Doctrine\DBAL\Platforms\AbstractPlatform;
6
use InvalidArgumentException;
7
 
8
use function array_filter;
9
use function array_keys;
10
use function array_map;
11
use function array_search;
12
use function array_shift;
13
use function count;
14
use function strtolower;
15
 
16
class Index extends AbstractAsset implements Constraint
17
{
18
    /**
19
     * Asset identifier instances of the column names the index is associated with.
20
     * array($columnName => Identifier)
21
     *
22
     * @var Identifier[]
23
     */
24
    protected $_columns = [];
25
 
26
    /** @var bool */
27
    protected $_isUnique = false;
28
 
29
    /** @var bool */
30
    protected $_isPrimary = false;
31
 
32
    /**
33
     * Platform specific flags for indexes.
34
     * array($flagName => true)
35
     *
36
     * @var true[]
37
     */
38
    protected $_flags = [];
39
 
40
    /**
41
     * Platform specific options
42
     *
43
     * @todo $_flags should eventually be refactored into options
44
     * @var mixed[]
45
     */
46
    private array $options;
47
 
48
    /**
49
     * @param string   $name
50
     * @param string[] $columns
51
     * @param bool     $isUnique
52
     * @param bool     $isPrimary
53
     * @param string[] $flags
54
     * @param mixed[]  $options
55
     */
56
    public function __construct(
57
        $name,
58
        array $columns,
59
        $isUnique = false,
60
        $isPrimary = false,
61
        array $flags = [],
62
        array $options = []
63
    ) {
64
        $isUnique = $isUnique || $isPrimary;
65
 
66
        $this->_setName($name);
67
        $this->_isUnique  = $isUnique;
68
        $this->_isPrimary = $isPrimary;
69
        $this->options    = $options;
70
 
71
        foreach ($columns as $column) {
72
            $this->_addColumn($column);
73
        }
74
 
75
        foreach ($flags as $flag) {
76
            $this->addFlag($flag);
77
        }
78
    }
79
 
80
    /** @throws InvalidArgumentException */
81
    protected function _addColumn(string $column): void
82
    {
83
        $this->_columns[$column] = new Identifier($column);
84
    }
85
 
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function getColumns()
90
    {
91
        return array_keys($this->_columns);
92
    }
93
 
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function getQuotedColumns(AbstractPlatform $platform)
98
    {
99
        $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
100
            ? $this->getOption('lengths') : [];
101
 
102
        $columns = [];
103
 
104
        foreach ($this->_columns as $column) {
105
            $length = array_shift($subParts);
106
 
107
            $quotedColumn = $column->getQuotedName($platform);
108
 
109
            if ($length !== null) {
110
                $quotedColumn .= '(' . $length . ')';
111
            }
112
 
113
            $columns[] = $quotedColumn;
114
        }
115
 
116
        return $columns;
117
    }
118
 
119
    /** @return string[] */
120
    public function getUnquotedColumns()
121
    {
122
        return array_map([$this, 'trimQuotes'], $this->getColumns());
123
    }
124
 
125
    /**
126
     * Is the index neither unique nor primary key?
127
     *
128
     * @return bool
129
     */
130
    public function isSimpleIndex()
131
    {
132
        return ! $this->_isPrimary && ! $this->_isUnique;
133
    }
134
 
135
    /** @return bool */
136
    public function isUnique()
137
    {
138
        return $this->_isUnique;
139
    }
140
 
141
    /** @return bool */
142
    public function isPrimary()
143
    {
144
        return $this->_isPrimary;
145
    }
146
 
147
    /**
148
     * @param string $name
149
     * @param int    $pos
150
     *
151
     * @return bool
152
     */
153
    public function hasColumnAtPosition($name, $pos = 0)
154
    {
155
        $name         = $this->trimQuotes(strtolower($name));
156
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
157
 
158
        return array_search($name, $indexColumns, true) === $pos;
159
    }
160
 
161
    /**
162
     * Checks if this index exactly spans the given column names in the correct order.
163
     *
164
     * @param string[] $columnNames
165
     *
166
     * @return bool
167
     */
168
    public function spansColumns(array $columnNames)
169
    {
170
        $columns         = $this->getColumns();
171
        $numberOfColumns = count($columns);
172
        $sameColumns     = true;
173
 
174
        for ($i = 0; $i < $numberOfColumns; $i++) {
175
            if (
176
                isset($columnNames[$i])
177
                && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))
178
            ) {
179
                continue;
180
            }
181
 
182
            $sameColumns = false;
183
        }
184
 
185
        return $sameColumns;
186
    }
187
 
188
    /**
189
     * Keeping misspelled function name for backwards compatibility
190
     *
191
     * @deprecated Use {@see isFulfilledBy()} instead.
192
     *
193
     * @return bool
194
     */
195
    public function isFullfilledBy(Index $other)
196
    {
197
        return $this->isFulfilledBy($other);
198
    }
199
 
200
    /**
201
     * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
202
     */
203
    public function isFulfilledBy(Index $other): bool
204
    {
205
        // allow the other index to be equally large only. It being larger is an option
206
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
207
        if (count($other->getColumns()) !== count($this->getColumns())) {
208
            return false;
209
        }
210
 
211
        // Check if columns are the same, and even in the same order
212
        $sameColumns = $this->spansColumns($other->getColumns());
213
 
214
        if ($sameColumns) {
215
            if (! $this->samePartialIndex($other)) {
216
                return false;
217
            }
218
 
219
            if (! $this->hasSameColumnLengths($other)) {
220
                return false;
221
            }
222
 
223
            if (! $this->isUnique() && ! $this->isPrimary()) {
224
                // this is a special case: If the current key is neither primary or unique, any unique or
225
                // primary key will always have the same effect for the index and there cannot be any constraint
226
                // overlaps. This means a primary or unique index can always fulfill the requirements of just an
227
                // index that has no constraints.
228
                return true;
229
            }
230
 
231
            if ($other->isPrimary() !== $this->isPrimary()) {
232
                return false;
233
            }
234
 
235
            return $other->isUnique() === $this->isUnique();
236
        }
237
 
238
        return false;
239
    }
240
 
241
    /**
242
     * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
243
     *
244
     * @return bool
245
     */
246
    public function overrules(Index $other)
247
    {
248
        if ($other->isPrimary()) {
249
            return false;
250
        }
251
 
252
        if ($this->isSimpleIndex() && $other->isUnique()) {
253
            return false;
254
        }
255
 
256
        return $this->spansColumns($other->getColumns())
257
            && ($this->isPrimary() || $this->isUnique())
258
            && $this->samePartialIndex($other);
259
    }
260
 
261
    /**
262
     * Returns platform specific flags for indexes.
263
     *
264
     * @return string[]
265
     */
266
    public function getFlags()
267
    {
268
        return array_keys($this->_flags);
269
    }
270
 
271
    /**
272
     * Adds Flag for an index that translates to platform specific handling.
273
     *
274
     * @param string $flag
275
     *
276
     * @return Index
277
     *
278
     * @example $index->addFlag('CLUSTERED')
279
     */
280
    public function addFlag($flag)
281
    {
282
        $this->_flags[strtolower($flag)] = true;
283
 
284
        return $this;
285
    }
286
 
287
    /**
288
     * Does this index have a specific flag?
289
     *
290
     * @param string $flag
291
     *
292
     * @return bool
293
     */
294
    public function hasFlag($flag)
295
    {
296
        return isset($this->_flags[strtolower($flag)]);
297
    }
298
 
299
    /**
300
     * Removes a flag.
301
     *
302
     * @param string $flag
303
     *
304
     * @return void
305
     */
306
    public function removeFlag($flag)
307
    {
308
        unset($this->_flags[strtolower($flag)]);
309
    }
310
 
311
    /**
312
     * @param string $name
313
     *
314
     * @return bool
315
     */
316
    public function hasOption($name)
317
    {
318
        return isset($this->options[strtolower($name)]);
319
    }
320
 
321
    /**
322
     * @param string $name
323
     *
324
     * @return mixed
325
     */
326
    public function getOption($name)
327
    {
328
        return $this->options[strtolower($name)];
329
    }
330
 
331
    /** @return mixed[] */
332
    public function getOptions()
333
    {
334
        return $this->options;
335
    }
336
 
337
    /**
338
     * Return whether the two indexes have the same partial index
339
     */
340
    private function samePartialIndex(Index $other): bool
341
    {
342
        if (
343
            $this->hasOption('where')
344
            && $other->hasOption('where')
345
            && $this->getOption('where') === $other->getOption('where')
346
        ) {
347
            return true;
348
        }
349
 
350
        return ! $this->hasOption('where') && ! $other->hasOption('where');
351
    }
352
 
353
    /**
354
     * Returns whether the index has the same column lengths as the other
355
     */
356
    private function hasSameColumnLengths(self $other): bool
357
    {
358
        $filter = static function (?int $length): bool {
359
            return $length !== null;
360
        };
361
 
362
        return array_filter($this->options['lengths'] ?? [], $filter)
363
            === array_filter($other->options['lengths'] ?? [], $filter);
364
    }
365
}