getDoctrineSchemaManager(); $table = $this->getTablePrefix().$blueprint->getTable(); $column = $connection->getDoctrineColumn($table, $command->from); $tableDiff = $this->getRenamedDiff($blueprint, $command, $column, $schema); return (array) $schema->getDatabasePlatform()->getAlterTableSQL($tableDiff); } /** * Get a new column instance with the new column name. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @param \Doctrine\DBAL\Schema\Column $column * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema * @return \Doctrine\DBAL\Schema\TableDiff */ protected function getRenamedDiff(Blueprint $blueprint, Fluent $command, Column $column, SchemaManager $schema) { $tableDiff = $this->getDoctrineTableDiff($blueprint, $schema); return $this->setRenamedColumns($tableDiff, $command, $column); } /** * Set the renamed columns on the table diff. * * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff * @param \Illuminate\Support\Fluent $command * @param \Doctrine\DBAL\Schema\Column $column * @return \Doctrine\DBAL\Schema\TableDiff */ protected function setRenamedColumns(TableDiff $tableDiff, Fluent $command, Column $column) { $newColumn = new Column($command->to, $column->getType(), $column->toArray()); $tableDiff->renamedColumns = array($command->from => $newColumn); return $tableDiff; } /** * Compile a foreign key command. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @return string */ public function compileForeign(Blueprint $blueprint, Fluent $command) { $table = $this->wrapTable($blueprint); $on = $this->wrapTable($command->on); // We need to prepare several of the elements of the foreign key definition // before we can create the SQL, such as wrapping the tables and convert // an array of columns to comma-delimited strings for the SQL queries. $columns = $this->columnize($command->columns); $onColumns = $this->columnize((array) $command->references); $sql = "alter table {$table} add constraint {$command->index} "; $sql .= "foreign key ({$columns}) references {$on} ({$onColumns})"; // Once we have the basic foreign key creation statement constructed we can // build out the syntax for what should happen on an update or delete of // the affected columns, which will get something like "cascade", etc. if ( ! is_null($command->onDelete)) { $sql .= " on delete {$command->onDelete}"; } if ( ! is_null($command->onUpdate)) { $sql .= " on update {$command->onUpdate}"; } return $sql; } /** * Compile the blueprint's column definitions. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @return array */ protected function getColumns(Blueprint $blueprint) { $columns = array(); foreach ($blueprint->getAddedColumns() as $column) { // Each of the column types have their own compiler functions which are tasked // with turning the column definition into its SQL format for this platform // used by the connection. The column's modifiers are compiled and added. $sql = $this->wrap($column).' '.$this->getType($column); $columns[] = $this->addModifiers($sql, $blueprint, $column); } return $columns; } /** * Add the column modifiers to the definition. * * @param string $sql * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $column * @return string */ protected function addModifiers($sql, Blueprint $blueprint, Fluent $column) { foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { $sql .= $this->{$method}($blueprint, $column); } } return $sql; } /** * Get the primary key command if it exists on the blueprint. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param string $name * @return \Illuminate\Support\Fluent|null */ protected function getCommandByName(Blueprint $blueprint, $name) { $commands = $this->getCommandsByName($blueprint, $name); if (count($commands) > 0) { return reset($commands); } } /** * Get all of the commands with a given name. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param string $name * @return array */ protected function getCommandsByName(Blueprint $blueprint, $name) { return array_filter($blueprint->getCommands(), function($value) use ($name) { return $value->name == $name; }); } /** * Get the SQL for the column data type. * * @param \Illuminate\Support\Fluent $column * @return string */ protected function getType(Fluent $column) { return $this->{"type".ucfirst($column->type)}($column); } /** * Add a prefix to an array of values. * * @param string $prefix * @param array $values * @return array */ public function prefixArray($prefix, array $values) { return array_map(function($value) use ($prefix) { return $prefix.' '.$value; }, $values); } /** * Wrap a table in keyword identifiers. * * @param mixed $table * @return string */ public function wrapTable($table) { if ($table instanceof Blueprint) $table = $table->getTable(); return parent::wrapTable($table); } /** * {@inheritdoc} */ public function wrap($value, $prefixAlias = false) { if ($value instanceof Fluent) $value = $value->name; return parent::wrap($value, $prefixAlias); } /** * Format a value so that it can be used in "default" clauses. * * @param mixed $value * @return string */ protected function getDefaultValue($value) { if ($value instanceof Expression) return $value; if (is_bool($value)) return "'".(int) $value."'"; return "'".strval($value)."'"; } /** * Create an empty Doctrine DBAL TableDiff from the Blueprint. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema * @return \Doctrine\DBAL\Schema\TableDiff */ protected function getDoctrineTableDiff(Blueprint $blueprint, SchemaManager $schema) { $table = $this->getTablePrefix().$blueprint->getTable(); $tableDiff = new TableDiff($table); $tableDiff->fromTable = $schema->listTableDetails($table); return $tableDiff; } /** * Compile a change column command into a series of SQL statements. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @param \Illuminate\Database\Connection $connection * @return array */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { $schema = $connection->getDoctrineSchemaManager(); $tableDiff = $this->getChangedDiff($blueprint, $schema); if ($tableDiff !== false) { return (array) $schema->getDatabasePlatform()->getAlterTableSQL($tableDiff); } return []; } /** * Get the Doctrine table difference for the given changes. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema * @return \Doctrine\DBAL\Schema\TableDiff|bool */ protected function getChangedDiff(Blueprint $blueprint, SchemaManager $schema) { $table = $schema->listTableDetails($this->getTablePrefix().$blueprint->getTable()); return (new Comparator)->diffTable($table, $this->getTableWithColumnChanges($blueprint, $table)); } /** * Get a copy of the given Doctrine table after making the column changes. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Doctrine\DBAL\Schema\Table $table * @return \Doctrine\DBAL\Schema\TableDiff */ protected function getTableWithColumnChanges(Blueprint $blueprint, Table $table) { $table = clone $table; foreach($blueprint->getChangedColumns() as $fluent) { $column = $this->getDoctrineColumnForChange($table, $fluent); // Here we will spin through each fluent column definition and map it to the proper // Doctrine column definitions, which is necessasry because Laravel and Doctrine // use some different terminology for various column attributes on the tables. foreach ($fluent->getAttributes() as $key => $value) { if ( ! is_null($option = $this->mapFluentOptionToDoctrine($key))) { if (method_exists($column, $method = 'set'.ucfirst($option))) { $column->{$method}($this->mapFluentValueToDoctrine($option, $value)); } } } } return $table; } /** * Get the Doctrine column instance for a column change. * * @param \Doctrine\DBAL\Schema\Table $table * @param \Illuminate\Support\Fluent $fluent * @return \Doctrine\DBAL\Schema\Column */ protected function getDoctrineColumnForChange(Table $table, Fluent $fluent) { return $table->changeColumn( $fluent['name'], $this->getDoctrineColumnChangeOptions($fluent) )->getColumn($fluent['name']); } /** * Get the Doctrine column change options. * * @param \Illuminate\Support\Fluent $fluent * @return array */ protected function getDoctrineColumnChangeOptions(Fluent $fluent) { $options = ['type' => $this->getDoctrineColumnType($fluent['type'])]; if (in_array($fluent['type'], ['text', 'mediumText', 'longText'])) { $options['length'] = $this->calculateDoctrineTextLength($fluent['type']); } return $options; } /** * Get the doctrine column type. * * @param string $type * @return \Doctrine\DBAL\Types\Type */ protected function getDoctrineColumnType($type) { $type = strtolower($type); switch ($type) { case 'biginteger': $type = 'bigint'; break; case 'smallinteger': $type = 'smallint'; break; case 'mediumtext': case 'longtext': $type = 'text'; break; } return Type::getType($type); } /** * Calculate the proper column length to force the Doctrine text type. * * @param string $type * @return int */ protected function calculateDoctrineTextLength($type) { switch ($type) { case 'mediumText': return 65535 + 1; case 'longText': return 16777215 + 1; default: return 255 + 1; } } /** * Get the matching Doctrine option for a given Fluent attribute name. * * @param string $attribute * @return string */ protected function mapFluentOptionToDoctrine($attribute) { switch($attribute) { case 'type': case 'name': return; case 'nullable': return 'notnull'; case 'total': return 'precision'; case 'places': return 'scale'; default: return $attribute; } } /** * Get the matching Doctrine value for a given Fluent attribute. * * @param string $option * @param mixed $value * @return mixed */ protected function mapFluentValueToDoctrine($option, $value) { return $option == 'notnull' ? ! $value : $value; } }