ChangeColumn.php
TLDR
The file ChangeColumn.php
is a part of the Illuminate\Database\Schema\Grammars
namespace. It provides the functionality to compile a change column command into a series of SQL statements. It also includes methods to get the Doctrine table difference for the given changes, get a copy of the given Doctrine table after making the column changes, and get the Doctrine column instance for a column change.
Methods
compile
This method compiles a change column command into a series of SQL statements. It takes a Grammar
object, a Blueprint
object, a Fluent
object, and a Connection
object as parameters. It throws a RuntimeException
if the Doctrine DBAL package is not installed. It returns an array of SQL statements.
getChangedDiff
This method gets the Doctrine table difference for the given changes. It takes a Grammar
object, a Blueprint
object, and a SchemaManager
object as parameters. It returns a TableDiff
object.
getTableWithColumnChanges
This method gets a copy of the given Doctrine table after making the column changes. It takes a Blueprint
object and a Table
object as parameters. It returns a modified Table
object.
getDoctrineColumn
This method gets the Doctrine column instance for a column change. It takes a Table
object and a Fluent
object as parameters. It returns a Column
object.
getDoctrineColumnChangeOptions
This method gets the Doctrine column change options. It takes a Fluent
object as a parameter. It returns an array of options.
getDoctrineColumnType
This method gets the Doctrine column type for a given type. It takes a string representing the type as a parameter. It returns a Type
object.
calculateDoctrineTextLength
This method calculates the proper column length to force the Doctrine text type. It takes a string representing the type as a parameter. It returns an integer representing the length.
doesntNeedCharacterOptions
This method determines if the given type does not need character/collation options. It takes a string representing the type as a parameter. It returns a boolean value.
mapFluentOptionToDoctrine
This method maps the Fluent attribute name to the corresponding Doctrine option. It takes a string representing the attribute as a parameter. It returns a string representing the Doctrine option.
mapFluentValueToDoctrine
This method maps the Fluent attribute value to the corresponding Doctrine value. It takes a string representing the option and a value as parameters. It returns the mapped Doctrine value.
<?php
namespace Illuminate\Database\Schema\Grammars;
use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
use RuntimeException;
class ChangeColumn
{
/**
* Compile a change column command into a series of SQL statements.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
*
* @throws \RuntimeException
*/
public static function compile($grammar, Blueprint $blueprint, Fluent $command, Connection $connection)
{
if (! $connection->isDoctrineAvailable()) {
throw new RuntimeException(sprintf(
'Changing columns for table "%s" requires Doctrine DBAL. Please install the doctrine/dbal package.',
$blueprint->getTable()
));
}
$schema = $connection->getDoctrineSchemaManager();
$databasePlatform = $connection->getDoctrineConnection()->getDatabasePlatform();
$databasePlatform->registerDoctrineTypeMapping('enum', 'string');
$tableDiff = static::getChangedDiff(
$grammar, $blueprint, $schema
);
if (! $tableDiff->isEmpty()) {
return (array) $databasePlatform->getAlterTableSQL($tableDiff);
}
return [];
}
/**
* Get the Doctrine table difference for the given changes.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
* @return \Doctrine\DBAL\Schema\TableDiff
*/
protected static function getChangedDiff($grammar, Blueprint $blueprint, SchemaManager $schema)
{
$current = $schema->introspectTable($grammar->getTablePrefix().$blueprint->getTable());
return $schema->createComparator()->compareTables(
$current, static::getTableWithColumnChanges($blueprint, $current)
);
}
/**
* 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\Table
*/
protected static function getTableWithColumnChanges(Blueprint $blueprint, Table $table)
{
$table = clone $table;
foreach ($blueprint->getChangedColumns() as $fluent) {
$column = static::getDoctrineColumn($table, $fluent);
// Here we will spin through each fluent column definition and map it to the proper
// Doctrine column definitions - which is necessary 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 = static::mapFluentOptionToDoctrine($key))) {
if (method_exists($column, $method = 'set'.ucfirst($option))) {
$column->{$method}(static::mapFluentValueToDoctrine($option, $value));
continue;
}
$column->setPlatformOption($option, static::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 static function getDoctrineColumn(Table $table, Fluent $fluent)
{
return $table->modifyColumn(
$fluent['name'], static::getDoctrineColumnChangeOptions($fluent)
)->getColumn($fluent['name']);
}
/**
* Get the Doctrine column change options.
*
* @param \Illuminate\Support\Fluent $fluent
* @return array
*/
protected static function getDoctrineColumnChangeOptions(Fluent $fluent)
{
$options = ['Type' => static::getDoctrineColumnType($fluent['type'])];
if (! in_array($fluent['type'], ['smallint', 'integer', 'bigint'])) {
$options['Autoincrement'] = false;
}
if (in_array($fluent['type'], ['tinyText', 'text', 'mediumText', 'longText'])) {
$options['Length'] = static::calculateDoctrineTextLength($fluent['type']);
}
if ($fluent['type'] === 'char') {
$options['Fixed'] = true;
}
if (static::doesntNeedCharacterOptions($fluent['type'])) {
$options['PlatformOptions'] = [
'collation' => '',
'charset' => '',
];
}
return $options;
}
/**
* Get the doctrine column type.
*
* @param string $type
* @return \Doctrine\DBAL\Types\Type
*/
protected static function getDoctrineColumnType($type)
{
$type = strtolower($type);
return Type::getType(match ($type) {
'biginteger' => 'bigint',
'smallinteger' => 'smallint',
'tinytext', 'mediumtext', 'longtext' => 'text',
'binary' => 'blob',
'uuid' => 'guid',
'char' => 'string',
'double' => 'float',
default => $type,
});
}
/**
* Calculate the proper column length to force the Doctrine text type.
*
* @param string $type
* @return int
*/
protected static function calculateDoctrineTextLength($type)
{
return match ($type) {
'tinyText' => 1,
'mediumText' => 65535 + 1,
'longText' => 16777215 + 1,
default => 255 + 1,
};
}
/**
* Determine if the given type does not need character / collation options.
*
* @param string $type
* @return bool
*/
protected static function doesntNeedCharacterOptions($type)
{
return in_array($type, [
'bigInteger',
'binary',
'boolean',
'date',
'dateTime',
'decimal',
'double',
'float',
'integer',
'json',
'mediumInteger',
'smallInteger',
'time',
'timestamp',
'tinyInteger',
]);
}
/**
* Get the matching Doctrine option for a given Fluent attribute name.
*
* @param string $attribute
* @return string|null
*/
protected static function mapFluentOptionToDoctrine($attribute)
{
return match ($attribute) {
'type', 'name' => null,
'nullable' => 'notnull',
'total' => 'precision',
'places' => 'scale',
default => $attribute,
};
}
/**
* Get the matching Doctrine value for a given Fluent attribute.
*
* @param string $option
* @param mixed $value
* @return mixed
*/
protected static function mapFluentValueToDoctrine($option, $value)
{
return $option === 'notnull' ? ! $value : $value;
}
}