Удаление столбца с внешним ключом Laravel error: General error: 1025 Error on rename

94

Я создал таблицу с помощью миграции следующим образом:

public function up()
{
    Schema::create('despatch_discrepancies',  function($table) {
        $table->increments('id')->unsigned();
        $table->integer('pick_id')->unsigned();
        $table->foreign('pick_id')->references('id')->on('picks');
        $table->integer('pick_detail_id')->unsigned();
        $table->foreign('pick_detail_id')->references('id')->on('pick_details');
        $table->integer('original_qty')->unsigned();
        $table->integer('shipped_qty')->unsigned();
    });
}

public function down()
{
    Schema::drop('despatch_discrepancies');
}

Мне нужно изменить эту таблицу и удалить ссылку и столбец внешнего ключа pick_detail_idи добавить новый столбец varchar, называемый skuпосле pick_idстолбца.

Итак, я создал еще одну миграцию, которая выглядит так:

public function up()
{
    Schema::table('despatch_discrepancies', function($table)
    {
        $table->dropForeign('pick_detail_id');
        $table->dropColumn('pick_detail_id');
        $table->string('sku', 20)->after('pick_id');
    });
}

public function down()
{
    Schema::table('despatch_discrepancies', function($table)
    {
        $table->integer('pick_detail_id')->unsigned();
        $table->foreign('pick_detail_id')->references('id')->on('pick_details');
        $table->dropColumn('sku');
    });
}

Когда я запускаю эту миграцию, я получаю следующую ошибку:

[Illuminate \ Database \ QueryException]
SQLSTATE [HY000]: общая ошибка: 1025 Ошибка при переименовании './dev_iwms_reboot/despatch_discrepancies' в './dev_iwms_reboot/#sql2-67c-17c464' (номер ошибки: 152) (SQL: изменить таблицу despatch_discrepanciesудалить внешний ключ pick_detail_id)

[PDOException]
SQLSTATE [HY000]: общая ошибка: 1025 Ошибка при переименовании './dev_iwms_reboot/despatch_discrepancies' в './dev_iwms_reboot/#sql2-67c-17c464' (ошибка: 152)

Когда я пытаюсь отменить эту миграцию, запустив php artisan migrate:rollbackкоманду, я получаю Rolled backсообщение, но на самом деле оно ничего не делает в базе данных.

Есть идеи, что может быть не так? Как удалить столбец со ссылкой на внешний ключ?

Latheesan
источник

Ответы:

167

Вы можете использовать это:

$table->dropForeign(['pick_detail_id']);
$table->dropColumn('pick_detail_id');

Если вы посмотрите на источник dropForeign, он построит для вас имя индекса внешнего ключа, если вы передадите имя столбца в виде массива.

Алекс Пинеда
источник
2
Принятый ответ тоже работает: вы должны использовать правильное соглашение об именах индексов. Но это проблема и с этим ответом: вы должны запомнить схему именования индексов, в то время как это решение делает это автоматически! Я всегда использовал другой способ и всегда жаловался на его непрактичность. Сейчас сразу перехожу на это решение. Большое спасибо!
Марко Палланте
6
Классный трюк. Я делал это долго, как лох. Laravel действительно может помочь в документации. Я могу принять вызов ...
simonhamp
1
У меня работал в Laravel 5.0. Большое спасибо, Алекс!
SilithCrowe
1
Работал как шарм в Laravel 5.2.
ronin1184
3
Это изящный трюк. Намного удобнее, чем запоминать соглашение об именах внешнего ключа (которое может измениться в будущем). Как сказал @ ronin1184, отлично работает в Laravel 5.2
Робин ван Баален
81

Оказывается; когда вы создаете внешний ключ следующим образом:

$table->integer('pick_detail_id')->unsigned();
$table->foreign('pick_detail_id')->references('id')->on('pick_details');

Laravel однозначно называет ссылку на внешний ключ следующим образом:

<table_name>_<foreign_table_name>_<column_name>_foreign
despatch_discrepancies_pick_detail_id_foreign (in my case)

Поэтому, когда вы хотите удалить столбец со ссылкой на внешний ключ, вы должны сделать это следующим образом:

$table->dropForeign('despatch_discrepancies_pick_detail_id_foreign');
$table->dropColumn('pick_detail_id');

Обновить:

Laravel 4.2+ вводит новое соглашение об именах:

<table_name>_<column_name>_foreign
Latheesan
источник
4
Не работает в Laravel 4.2. <foreign_table_name> не является частью имени ключа. Он работает только с <table_name> _ <column_name> _foreign.
Rich Remer
Я использовал его в laravel 4.2 и до сих пор использую, он работает для меня.
Latheesan,
2
<table_name>_<column_name>_foreignКонвенция по- прежнему кажется, работает на 5,1
Яхья Uddin
По-видимому, после снятия ограничения на отношения вам также нужно удалить столбец. Я думаю, что документация тоже должна была включать это, потому что можно легко предположить, что dropForeign также удалит столбец. спасибо за то, что поделился. laravel.com/docs/5.0/schema#dropping-columns
пикрасм
Если кому-то интересно, индексы, которые MySQL автоматически создает для внешних ключей, удаляются вместе с столбцами. Нет необходимости сбрасывать их вручную с помощью $table->dropIndex('column_name').
Aleksandar
24

У меня было несколько внешних ключей в моей таблице, и затем мне пришлось удалить ограничения внешнего ключа один за другим, передав имя столбца в качестве индекса массива в методе down:

public function up()
{
    Schema::table('offices', function (Blueprint $table) {
        $table->unsignedInteger('country_id')->nullable();
        $table->foreign('country_id')
            ->references('id')
            ->on('countries')
            ->onDelete('cascade');

        $table->unsignedInteger('stateprovince_id')->nullable();
        $table->foreign('stateprovince_id')
            ->references('id')
            ->on('stateprovince')
            ->onDelete('cascade');
        $table->unsignedInteger('city_id')->nullable();
        $table->foreign('city_id')
            ->references('id')
            ->on('cities')
            ->onDelete('cascade');
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    Schema::table('offices', function (Blueprint $table) {
        $table->dropForeign(['country_id']);
        $table->dropForeign(['stateprovince_id']);
        $table->dropForeign(['city_id']);
        $table->dropColumn(['country_id','stateprovince_id','city_id']);
    });
} 

Использование инструкции ниже не работает

$table->dropForeign(['country_id','stateprovince_id','city_id']); 

Потому что dropForeign не считает их отдельными столбцами, которые мы хотим удалить. Поэтому мы должны отбрасывать их по одному.

Афраз Ахмад
источник
Спасибо, мой друг, добавление имени столбца в массив работает для меня.
Pierre
Если кому-то интересно, индексы, которые MySQL автоматически создает для внешних ключей, удаляются вместе с столбцами. Нет необходимости сбрасывать их вручную с помощью $table->dropIndex('column_name').
Aleksandar
9

Ключом (для меня) к решению этой проблемы было убедиться, что команде $ table-> dropForeign () передается правильное имя отношения, не обязательно имя столбца. Вы не хотите передавать имя столбца, поскольку это было бы гораздо более интуитивно понятным ИМХО.

Для меня сработало:

$table->dropForeign('local_table_foreign_id_foreign');
$table->column('foreign_id');

Итак, строка, которую я передал в dropForeign (), которая сработала для меня, была в формате:

[локальная таблица] _ [поле внешнего ключа] _foreign

Если у вас есть доступ к такому инструменту, как Sequel Pro или Navicat, возможность визуализировать их будет очень полезна.

DirtyBirdNJ
источник
Это отлично работает, я просто обнаружил, что это менее интуитивно понятно, чем заключать таблицу в скобки, как предложил @Alex.
Марк Караван,
5

Мне пришло в голову, что я не знал, куда положить Schema::table блок.

Позже я обнаружил, что ключ находится в ошибке SQL:

[Illuminate\Database\QueryException]
SQLSTATE[23000]: Integrity constraint violation: 1217 Cannot delete or update a parent row: a foreign key constraint fails (SQL: drop table if exists `lu_benefits_categories`)

Таким образом, Schema::tableблок должен идти в down()функции lu_benefits_categoriesмиграции и перед Schema::dropIfExistsстрокой:

public function down()
{
    Schema::table('table', function (Blueprint $table) {
        $table->dropForeign('table_category_id_foreign');
        $table->dropColumn('category_id');
    });
    Schema::dropIfExists('lu_benefits_categories');
}

После этого php artisan migrate:refreshили php artisan migrate:resetсделает свое дело.

Гас
источник