简体   繁体   中英

Column already exists: 1060 when modifying column to have a unique index in Laravel with migrations

Today I was trying to create a string column for a slug in one of my tables. I required the column to have a unique index, but the table had information already, so, the moment the column was created, I got an error message informing me of duplicate and empty values in the new column.

public function up(){
  Schema::table( 'states', function( Blueprint $table ){
    $table->string( 'slug', 128 )->unique();
  } );
}

Since the slug was going to contain the slugified version of the 'name' column, I decided to create the column in two steps. First the column was going to be a regular column and I was going to cycle through all the records creating the correct slug value using Str::slug(). After that I was going to modify the column to add the unique() option following Laravel's documentation :

public function up(){
  Schema::table( 'states', function( Blueprint $table ){
    $table->string( 'slug', 128 );
    $states = State::all();
    foreach( $states as $state ){
      $state->slug = Str::slug( $state->name );
      $state->save();
    }
    $table->string( 'slug', 128 )->unique()->change();
  } );
}

I was thinking this was a fine solution, because I was not going to break any rules in my database since all the names were different. I ran the migration and I got a nice error:

SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name 'slug' (SQL: alter table `states` add `slug` varchar(128) not null)

Any ideas on how can I create correctly the migration?

Change the line:

$table->string( 'slug', 128 )->unique()->change();

to

$table->unique( 'slug' );

All together:

public function up(){
  Schema::table( 'states', function( Blueprint $table ){
    $table->string( 'slug', 128 );

    foreach( State::all() as $state ){
      $state->update(['slug' => = Str::slug( $state->name )]);
    }

    $table->unique( 'slug' );
  } );
}

See https://laravel.com/docs/7.x/migrations#creating-indexes .

After trying # DigitalDrifter 's answer without success (I kept getting the same error), I started wondering if the problem was that all my column modifications were executed inside the same method function( Blueprint $table ) , so I decided to separate the process into 3 steps like this:

public function up(){
  Schema::table( 'states', function( Blueprint $table ){
    $table->string( 'slug', 128 );
  } );

  $states = State::all();
  foreach( $states as $state ){
    $state->slug = Str::slug( $state->state );
    $state->save();
  }

  Schema::table( 'states', function( Blueprint $table ){
    $table->string( 'slug', 128 )->unique()->change();
  } );
}

This version of my code was successful, by the way, # DigitalDrifter 's suggestion was also correct, I tested $table->unique( 'slug' ); with my approach and it also works.

There are two problems here, one is adding a duplicate column and applying unique index and second is possibly the collision in method Str::slug

before adding column or unique index check if it doesn't exist already

public function up() {
    \DB::transaction(function() {
        if (!Schema::hasColumn('states', 'slug')) {
            Schema::table('states', function (Blueprint $table) {
                $table->string('slug', 128);
                $table->unique('slug', 'unique_slug_index');
            });
        }

        $states = State::all();
        foreach( $states as $state ){
            $state->slug = Str::slug( $state->name );
            $state->save();
        }
    });
}

\DB:transaction() clause automatically rollback your migration if it gets failed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM