Eliminando coluna com chave estrangeira Erro do Laravel: Erro geral: 1025 Erro ao renomear

96

Eu criei uma tabela usando a migração como esta:

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');
}

Eu preciso mudar esta tabela e descartar a referência de chave estrangeira e coluna pick_detail_ide adicionar uma nova coluna varchar chamada skuapós pick_idcoluna.

Então, criei outra migração, que se parece com isto:

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');
    });
}

Quando executo essa migração, recebo o seguinte erro:

[Illuminate \ Database \ QueryException]
SQLSTATE [HY000]: Erro geral: 1025 Erro na renomeação de './dev_iwms_reboot/despatch_discrepancies' para './dev_iwms_reboot/#sql2-67c-17c464' (errno: 152) (tabela SQL: alter despatch_discrepancieslargar chave estrangeira pick_detail_id)

[PDOException]
SQLSTATE [HY000]: Erro geral: 1025 Erro na renomeação de './dev_iwms_reboot/despatch_discrepancies' para './dev_iwms_reboot/#sql2-67c-17c464' (errno: 152)

Quando tento reverter essa migração executando o php artisan migrate:rollbackcomando, recebo uma Rolled backmensagem, mas na verdade não está fazendo nada no banco de dados.

Alguma ideia do que pode estar errado? Como você elimina uma coluna que tem uma referência de chave estrangeira?

Latheesan
fonte

Respostas:

172

Você pode usar isto:

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

Se você der um pico na fonte dropForeign, ele construirá o nome do índice da chave estrangeira para você se você passar o nome da coluna como um array.

Alex Pineda
fonte
3
A resposta aceita também funciona: você deve usar a convenção de nomes de índice correta. Mas esse é o problema com essa resposta também: você tem que lembrar o esquema de nomenclatura para índices, enquanto esta solução o faz automaticamente! Eu sempre usei o outro caminho e sempre reclamei sobre como isso era pouco prático. Agora estou mudando imediatamente para esta solução. Muito obrigado!
Marco Pallante
7
Um truque incrível. Tenho feito isso há muito tempo como um otário. O Laravel realmente precisa de alguma ajuda na documentação. Posso aceitar o desafio ...
simonhamp
1
Trabalhou para mim no Laravel 5.0. Muito obrigado, Alex!
SilithCrowe de
1
Funcionou como um encanto no Laravel 5.2.
ronin1184
3
Este é um truque legal. Muito mais amigável do que lembrar a convenção de nomenclatura de chave estrangeira (que pode mudar no futuro). Como @ ronin1184 disse, funciona perfeitamente no Laravel 5.2
Robin van Baalen
81

Acontece que; quando você cria uma chave estrangeira como esta:

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

O Laravel nomeia exclusivamente a referência de chave estrangeira assim:

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

Portanto, quando você deseja eliminar uma coluna com referência de chave estrangeira, você deve fazer desta forma:

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

Atualizar:

Laravel 4.2+ introduz uma nova convenção de nomenclatura:

<table_name>_<column_name>_foreign
Latheesan
fonte
4
Não funciona no Laravel 4.2. <foreign_table_name> não faz parte do nome da chave. Funciona apenas com <table_name> _ <column_name> _foreign.
rich remer
Eu usei no laravel 4.2 e ainda uso, funciona para mim.
Latheesan
2
A <table_name>_<column_name>_foreignconvenção ainda parece funcionar para 5.1
Yahya Uddin
Aparentemente, depois de descartar a restrição no relacionamento, você também precisa descartar a coluna. Acho que a documentação deveria ter incluído isso também, porque pode-se presumir facilmente que dropForeign também excluirá a coluna. obrigado pela partilha. laravel.com/docs/5.0/schema#dropping-columns
Picrasma
Se alguém estava se perguntando, os índices que o MySQL cria automaticamente para chaves estrangeiras são descartados quando as colunas são removidas. Não há necessidade de soltá-los manualmente com $table->dropIndex('column_name').
Aleksandar
25

Eu tinha várias chaves estrangeiras em minha tabela e, em seguida, tive que remover as restrições de chave estrangeira, uma por uma, passando o nome da coluna como índice do array no método 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']);
    });
} 

Usar a declaração abaixo não funciona

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

Porque dropForeign não os considera colunas separadas que queremos remover. Portanto, temos que soltá-los um por um.

Afraz Ahmad
fonte
Obrigado meu amigo, adicionar o nome da coluna em uma matriz funciona para mim.
Pierre
Se alguém está se perguntando, os índices que o MySQL cria automaticamente para chaves estrangeiras são descartados quando as colunas são. Não há necessidade de soltá-los manualmente com $table->dropIndex('column_name').
Aleksandar
9

A chave (para mim) para resolver isso era ter certeza de que o comando $ table-> dropForeign () estava recebendo o nome de relacionamento correto, não necessariamente o nome da coluna. Você não quer passar o nome da coluna, pois seria muito mais intuitivo IMHO.

O que funcionou para mim foi:

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

Portanto, a string que passei para dropForeign () que funcionou para mim estava no formato de:

[tabela local] _ [campo de chave estrangeira] _foreign

Se você tiver acesso a uma ferramenta como Sequel Pro ou Navicat, poder visualizá-las será muito útil.

DirtyBirdNJ
fonte
Isso funciona bem, eu apenas descobri que é menos intuitivo do que colocar a tabela entre colchetes, conforme sugerido por @Alex.
Mark Karavan
6

Algo que me ocorreu foi que eu não sabia onde colocar o Schema::tablebloqueio.

Mais tarde descobri que a chave está no erro 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`)

Então o Schema::tablebloco precisa ir na down()função da lu_benefits_categoriesmigração e antes da Schema::dropIfExistslinha:

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

Depois disso, o php artisan migrate:refreshou php artisan migrate:resetfará o truque.

Gus
fonte