459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
<?php namespace Illuminate\Database\Schema\Grammars;
 | 
						|
 | 
						|
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 = 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;
 | 
						|
	}
 | 
						|
 | 
						|
}
 |