/vendor/laravel/framework/src/Illuminate/Database/Schema/Grammars/Grammar.php
PHP | 466 lines | 225 code | 72 blank | 169 comment | 18 complexity | db4ef517bf6719ba3fb9fb1ba0c17c06 MD5 | raw file
- <?php
- namespace Illuminate\Database\Schema\Grammars;
- use RuntimeException;
- use Doctrine\DBAL\Types\Type;
- use Illuminate\Support\Fluent;
- use Doctrine\DBAL\Schema\Table;
- use Doctrine\DBAL\Schema\Column;
- use Doctrine\DBAL\Schema\TableDiff;
- use Illuminate\Database\Connection;
- use Doctrine\DBAL\Schema\Comparator;
- use Illuminate\Database\Query\Expression;
- use Illuminate\Database\Schema\Blueprint;
- use Illuminate\Database\Grammar as BaseGrammar;
- use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
- abstract class Grammar extends BaseGrammar
- {
- /**
- * Compile a rename column command.
- *
- * @param \Illuminate\Database\Schema\Blueprint $blueprint
- * @param \Illuminate\Support\Fluent $command
- * @param \Illuminate\Database\Connection $connection
- * @return array
- */
- public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
- {
- $schema = $connection->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 = [$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);
- $index = $this->wrap($command->index);
- $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 {$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 = [];
- 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
- *
- * @throws \RuntimeException
- */
- public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection)
- {
- if (! $connection->isDoctrineAvailable()) {
- throw new RuntimeException(sprintf(
- 'Changing columns for table "%s" requires Doctrine DBAL; install "doctrine/dbal".',
- $blueprint->getTable()
- ));
- }
- $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 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 = $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;
- case 'binary':
- $type = 'blob';
- 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|null
- */
- 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;
- }
- }