Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5 migration: error while renaming columns

I'm new to Laravel and have a migration like this:

public function up()
{
    Schema::table('mytable', function(Blueprint $table)
    {
        $table->renameColumn('mycol', 'old_mycol');
        $table->string('mycol', 100);
    });
}

When I run it, I get the error:

[PDOException]
SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name 'mycol'

I ended up splitting it into 2 separate migrations and that worked fine, but I don't understand why doing it in one pass is a problem.

like image 541
dorian108 Avatar asked Mar 17 '23 01:03

dorian108


1 Answers

This is because Laravel will implicitly put any commands that add new or modify existing columns at the very beginning of the commands array when the migration is executed. The following code is taken directly from the Illuminate\Database\Schema\Blueprint class.

/**
 * Get the raw SQL statements for the blueprint.
 *
 * @param  \Illuminate\Database\Connection  $connection
 * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
 * @return array
 */
public function toSql(Connection $connection, Grammar $grammar)
{
    $this->addImpliedCommands();

    $statements = array();

    // Each type of command has a corresponding compiler function on the schema
    // grammar which is used to build the necessary SQL statements to build
    // the blueprint element, so we'll just call that compilers function.
    foreach ($this->commands as $command)
    {
        $method = 'compile'.ucfirst($command->name);

        if (method_exists($grammar, $method))
        {
            if ( ! is_null($sql = $grammar->$method($this, $command, $connection)))
            {
                $statements = array_merge($statements, (array) $sql);
            }
        }
    }

    return $statements;
}

/**
 * Add the commands that are implied by the blueprint.
 *
 * @return void
 */
protected function addImpliedCommands()
{
    if (count($this->getAddedColumns()) > 0 && ! $this->creating())
    {
        array_unshift($this->commands, $this->createCommand('add'));
    }

    if (count($this->getChangedColumns()) > 0 && ! $this->creating())
    {
        array_unshift($this->commands, $this->createCommand('change'));
    }

    $this->addFluentIndexes();
}

As you can see from the code above, in the toSql method, there's a call to addImpliedCommands in which several commands might get added to the beginning of the commands array of the object. This causes the command for your new mycol column to be executed first before the rename command.

To get around this, you don't really need to create two migrations. In the same migration, you can simply call Schema::table() twice like so:

Schema::table('mytable', function(Blueprint $table)
{
    $table->renameColumn('mycol', 'old_mycol');
});

Schema::table('mytable', function(Blueprint $table)
{
    $table->string('mycol', 100);
});
like image 99
Kemal Fadillah Avatar answered Mar 19 '23 04:03

Kemal Fadillah