Skip to content

Commit 462af93

Browse files
fix: drop_fk op key + truncate overlong index names
- DiffEngine: emit 'fk_name' (not 'fk') for drop_fk operations so drivers can read it. Prevents 'Undefined array key fk_name' warning and empty DROP FOREIGN KEY statements. - ModelScanner::addIndexes: truncate auto-generated index names that exceed MySQL's 64-char identifier limit, appending an 8-char md5 suffix for uniqueness. Prevents 'Identifier name too long' errors on tables with long names + composite indexes.
1 parent a929d0a commit 462af93

2 files changed

Lines changed: 23 additions & 8 deletions

File tree

‎src/Database/Migrations/DiffEngine.php‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,23 +258,23 @@ private function diffForeignKeys(MigrationPlan $plan, TableDef $modelTable, Tabl
258258
// Drop and recreate the foreign key
259259
$plan->addOperation('drop_fk', [
260260
'table' => $modelTable->name,
261-
'fk' => $fkName,
261+
'fk_name' => $fkName,
262262
'destructive' => false
263263
]);
264-
264+
265265
$plan->addOperation('add_fk', [
266266
'table' => $modelTable->name,
267267
'fk' => $modelFk->toArray(),
268268
'destructive' => false
269269
]);
270270
}
271271
}
272-
272+
273273
// Drop removed foreign keys
274274
foreach ($fksToDrop as $fkName) {
275275
$plan->addOperation('drop_fk', [
276276
'table' => $modelTable->name,
277-
'fk' => $fkName,
277+
'fk_name' => $fkName,
278278
'destructive' => false
279279
]);
280280
}

‎src/Database/Migrations/ModelScanner.php‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,36 +417,51 @@ private function addIndexes(TableDef $table, ReflectionClass $reflection): void
417417
'fulltext' => 'ft_',
418418
default => 'idx_'
419419
};
420-
$indexName = $prefix . $table->name . '_' . $key;
420+
$indexName = $this->truncateIdentifier($prefix . $table->name . '_' . $key);
421421
$table->indexes[$indexName] = new IndexDef($indexName, $key, $value);
422422
} elseif (is_array($value)) {
423423
// Composite index: numeric key with array value
424424
// Extract type if specified, otherwise default to 'index'
425425
$type = 'index';
426426
$columns = [];
427-
427+
428428
foreach ($value as $k => $v) {
429429
if ($k === 'type') {
430430
$type = $v;
431431
} elseif (is_int($k)) {
432432
$columns[] = $v;
433433
}
434434
}
435-
435+
436436
if ($columns !== []) {
437437
$columnSuffix = implode('_', $columns);
438438
$prefix = match ($type) {
439439
'unique' => 'uniq_',
440440
'fulltext' => 'ft_',
441441
default => 'idx_'
442442
};
443-
$indexName = $prefix . $table->name . '_' . $columnSuffix;
443+
$indexName = $this->truncateIdentifier($prefix . $table->name . '_' . $columnSuffix);
444444
$table->indexes[$indexName] = new IndexDef($indexName, $columns, $type);
445445
}
446446
}
447447
}
448448
}
449449

450+
/**
451+
* Truncate an identifier to fit within MySQL's 64-character limit.
452+
* When truncation is needed, append an 8-char hash of the full name to preserve uniqueness.
453+
*/
454+
private function truncateIdentifier(string $name, int $maxLength = 64): string
455+
{
456+
if (strlen($name) <= $maxLength) {
457+
return $name;
458+
}
459+
460+
$hash = substr(md5($name), 0, 8);
461+
$prefixLength = $maxLength - strlen($hash) - 1; // reserve space for '_' + hash
462+
return substr($name, 0, $prefixLength) . '_' . $hash;
463+
}
464+
450465
/**
451466
* @param ReflectionClass<object> $reflection
452467
*/

0 commit comments

Comments
 (0)