PageRenderTime 33ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/laravel/framework/src/Illuminate/Database/Schema/Grammars/Grammar.php

https://gitlab.com/mdabutaleb/bitm-laravel-1
PHP | 459 lines | 225 code | 65 blank | 169 comment | 18 complexity | 4f13ea83c1de8f668d67af6293a08601 MD5 | raw file
  1. <?php
  2. namespace Illuminate\Database\Schema\Grammars;
  3. use RuntimeException;
  4. use Doctrine\DBAL\Types\Type;
  5. use Illuminate\Support\Fluent;
  6. use Doctrine\DBAL\Schema\Table;
  7. use Doctrine\DBAL\Schema\Column;
  8. use Doctrine\DBAL\Schema\TableDiff;
  9. use Illuminate\Database\Connection;
  10. use Doctrine\DBAL\Schema\Comparator;
  11. use Illuminate\Database\Query\Expression;
  12. use Illuminate\Database\Schema\Blueprint;
  13. use Illuminate\Database\Grammar as BaseGrammar;
  14. use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
  15. abstract class Grammar extends BaseGrammar
  16. {
  17. /**
  18. * Compile a rename column command.
  19. *
  20. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  21. * @param \Illuminate\Support\Fluent $command
  22. * @param \Illuminate\Database\Connection $connection
  23. * @return array
  24. */
  25. public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
  26. {
  27. $schema = $connection->getDoctrineSchemaManager();
  28. $table = $this->getTablePrefix().$blueprint->getTable();
  29. $column = $connection->getDoctrineColumn($table, $command->from);
  30. $tableDiff = $this->getRenamedDiff($blueprint, $command, $column, $schema);
  31. return (array) $schema->getDatabasePlatform()->getAlterTableSQL($tableDiff);
  32. }
  33. /**
  34. * Get a new column instance with the new column name.
  35. *
  36. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  37. * @param \Illuminate\Support\Fluent $command
  38. * @param \Doctrine\DBAL\Schema\Column $column
  39. * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
  40. * @return \Doctrine\DBAL\Schema\TableDiff
  41. */
  42. protected function getRenamedDiff(Blueprint $blueprint, Fluent $command, Column $column, SchemaManager $schema)
  43. {
  44. $tableDiff = $this->getDoctrineTableDiff($blueprint, $schema);
  45. return $this->setRenamedColumns($tableDiff, $command, $column);
  46. }
  47. /**
  48. * Set the renamed columns on the table diff.
  49. *
  50. * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff
  51. * @param \Illuminate\Support\Fluent $command
  52. * @param \Doctrine\DBAL\Schema\Column $column
  53. * @return \Doctrine\DBAL\Schema\TableDiff
  54. */
  55. protected function setRenamedColumns(TableDiff $tableDiff, Fluent $command, Column $column)
  56. {
  57. $newColumn = new Column($command->to, $column->getType(), $column->toArray());
  58. $tableDiff->renamedColumns = [$command->from => $newColumn];
  59. return $tableDiff;
  60. }
  61. /**
  62. * Compile a foreign key command.
  63. *
  64. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  65. * @param \Illuminate\Support\Fluent $command
  66. * @return string
  67. */
  68. public function compileForeign(Blueprint $blueprint, Fluent $command)
  69. {
  70. $table = $this->wrapTable($blueprint);
  71. $index = $this->wrap($command->index);
  72. $on = $this->wrapTable($command->on);
  73. // We need to prepare several of the elements of the foreign key definition
  74. // before we can create the SQL, such as wrapping the tables and convert
  75. // an array of columns to comma-delimited strings for the SQL queries.
  76. $columns = $this->columnize($command->columns);
  77. $onColumns = $this->columnize((array) $command->references);
  78. $sql = "alter table {$table} add constraint {$index} ";
  79. $sql .= "foreign key ({$columns}) references {$on} ({$onColumns})";
  80. // Once we have the basic foreign key creation statement constructed we can
  81. // build out the syntax for what should happen on an update or delete of
  82. // the affected columns, which will get something like "cascade", etc.
  83. if (! is_null($command->onDelete)) {
  84. $sql .= " on delete {$command->onDelete}";
  85. }
  86. if (! is_null($command->onUpdate)) {
  87. $sql .= " on update {$command->onUpdate}";
  88. }
  89. return $sql;
  90. }
  91. /**
  92. * Compile the blueprint's column definitions.
  93. *
  94. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  95. * @return array
  96. */
  97. protected function getColumns(Blueprint $blueprint)
  98. {
  99. $columns = [];
  100. foreach ($blueprint->getAddedColumns() as $column) {
  101. // Each of the column types have their own compiler functions which are tasked
  102. // with turning the column definition into its SQL format for this platform
  103. // used by the connection. The column's modifiers are compiled and added.
  104. $sql = $this->wrap($column).' '.$this->getType($column);
  105. $columns[] = $this->addModifiers($sql, $blueprint, $column);
  106. }
  107. return $columns;
  108. }
  109. /**
  110. * Add the column modifiers to the definition.
  111. *
  112. * @param string $sql
  113. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  114. * @param \Illuminate\Support\Fluent $column
  115. * @return string
  116. */
  117. protected function addModifiers($sql, Blueprint $blueprint, Fluent $column)
  118. {
  119. foreach ($this->modifiers as $modifier) {
  120. if (method_exists($this, $method = "modify{$modifier}")) {
  121. $sql .= $this->{$method}($blueprint, $column);
  122. }
  123. }
  124. return $sql;
  125. }
  126. /**
  127. * Get the primary key command if it exists on the blueprint.
  128. *
  129. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  130. * @param string $name
  131. * @return \Illuminate\Support\Fluent|null
  132. */
  133. protected function getCommandByName(Blueprint $blueprint, $name)
  134. {
  135. $commands = $this->getCommandsByName($blueprint, $name);
  136. if (count($commands) > 0) {
  137. return reset($commands);
  138. }
  139. }
  140. /**
  141. * Get all of the commands with a given name.
  142. *
  143. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  144. * @param string $name
  145. * @return array
  146. */
  147. protected function getCommandsByName(Blueprint $blueprint, $name)
  148. {
  149. return array_filter($blueprint->getCommands(), function ($value) use ($name) {
  150. return $value->name == $name;
  151. });
  152. }
  153. /**
  154. * Get the SQL for the column data type.
  155. *
  156. * @param \Illuminate\Support\Fluent $column
  157. * @return string
  158. */
  159. protected function getType(Fluent $column)
  160. {
  161. return $this->{'type'.ucfirst($column->type)}($column);
  162. }
  163. /**
  164. * Add a prefix to an array of values.
  165. *
  166. * @param string $prefix
  167. * @param array $values
  168. * @return array
  169. */
  170. public function prefixArray($prefix, array $values)
  171. {
  172. return array_map(function ($value) use ($prefix) {
  173. return $prefix.' '.$value;
  174. }, $values);
  175. }
  176. /**
  177. * Wrap a table in keyword identifiers.
  178. *
  179. * @param mixed $table
  180. * @return string
  181. */
  182. public function wrapTable($table)
  183. {
  184. if ($table instanceof Blueprint) {
  185. $table = $table->getTable();
  186. }
  187. return parent::wrapTable($table);
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function wrap($value, $prefixAlias = false)
  193. {
  194. if ($value instanceof Fluent) {
  195. $value = $value->name;
  196. }
  197. return parent::wrap($value, $prefixAlias);
  198. }
  199. /**
  200. * Format a value so that it can be used in "default" clauses.
  201. *
  202. * @param mixed $value
  203. * @return string
  204. */
  205. protected function getDefaultValue($value)
  206. {
  207. if ($value instanceof Expression) {
  208. return $value;
  209. }
  210. if (is_bool($value)) {
  211. return "'".(int) $value."'";
  212. }
  213. return "'".strval($value)."'";
  214. }
  215. /**
  216. * Create an empty Doctrine DBAL TableDiff from the Blueprint.
  217. *
  218. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  219. * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
  220. * @return \Doctrine\DBAL\Schema\TableDiff
  221. */
  222. protected function getDoctrineTableDiff(Blueprint $blueprint, SchemaManager $schema)
  223. {
  224. $table = $this->getTablePrefix().$blueprint->getTable();
  225. $tableDiff = new TableDiff($table);
  226. $tableDiff->fromTable = $schema->listTableDetails($table);
  227. return $tableDiff;
  228. }
  229. /**
  230. * Compile a change column command into a series of SQL statements.
  231. *
  232. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  233. * @param \Illuminate\Support\Fluent $command
  234. * @param \Illuminate\Database\Connection $connection
  235. * @return array
  236. *
  237. * @throws \RuntimeException
  238. */
  239. public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection)
  240. {
  241. if (! $connection->isDoctrineAvailable()) {
  242. throw new RuntimeException(sprintf(
  243. 'Changing columns for table "%s" requires Doctrine DBAL; install "doctrine/dbal".',
  244. $blueprint->getTable()
  245. ));
  246. }
  247. $schema = $connection->getDoctrineSchemaManager();
  248. $tableDiff = $this->getChangedDiff($blueprint, $schema);
  249. if ($tableDiff !== false) {
  250. return (array) $schema->getDatabasePlatform()->getAlterTableSQL($tableDiff);
  251. }
  252. return [];
  253. }
  254. /**
  255. * Get the Doctrine table difference for the given changes.
  256. *
  257. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  258. * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
  259. * @return \Doctrine\DBAL\Schema\TableDiff|bool
  260. */
  261. protected function getChangedDiff(Blueprint $blueprint, SchemaManager $schema)
  262. {
  263. $table = $schema->listTableDetails($this->getTablePrefix().$blueprint->getTable());
  264. return (new Comparator)->diffTable($table, $this->getTableWithColumnChanges($blueprint, $table));
  265. }
  266. /**
  267. * Get a copy of the given Doctrine table after making the column changes.
  268. *
  269. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  270. * @param \Doctrine\DBAL\Schema\Table $table
  271. * @return \Doctrine\DBAL\Schema\TableDiff
  272. */
  273. protected function getTableWithColumnChanges(Blueprint $blueprint, Table $table)
  274. {
  275. $table = clone $table;
  276. foreach ($blueprint->getChangedColumns() as $fluent) {
  277. $column = $this->getDoctrineColumnForChange($table, $fluent);
  278. // Here we will spin through each fluent column definition and map it to the proper
  279. // Doctrine column definitions - which is necessary because Laravel and Doctrine
  280. // use some different terminology for various column attributes on the tables.
  281. foreach ($fluent->getAttributes() as $key => $value) {
  282. if (! is_null($option = $this->mapFluentOptionToDoctrine($key))) {
  283. if (method_exists($column, $method = 'set'.ucfirst($option))) {
  284. $column->{$method}($this->mapFluentValueToDoctrine($option, $value));
  285. }
  286. }
  287. }
  288. }
  289. return $table;
  290. }
  291. /**
  292. * Get the Doctrine column instance for a column change.
  293. *
  294. * @param \Doctrine\DBAL\Schema\Table $table
  295. * @param \Illuminate\Support\Fluent $fluent
  296. * @return \Doctrine\DBAL\Schema\Column
  297. */
  298. protected function getDoctrineColumnForChange(Table $table, Fluent $fluent)
  299. {
  300. return $table->changeColumn(
  301. $fluent['name'], $this->getDoctrineColumnChangeOptions($fluent)
  302. )->getColumn($fluent['name']);
  303. }
  304. /**
  305. * Get the Doctrine column change options.
  306. *
  307. * @param \Illuminate\Support\Fluent $fluent
  308. * @return array
  309. */
  310. protected function getDoctrineColumnChangeOptions(Fluent $fluent)
  311. {
  312. $options = ['type' => $this->getDoctrineColumnType($fluent['type'])];
  313. if (in_array($fluent['type'], ['text', 'mediumText', 'longText'])) {
  314. $options['length'] = $this->calculateDoctrineTextLength($fluent['type']);
  315. }
  316. return $options;
  317. }
  318. /**
  319. * Get the doctrine column type.
  320. *
  321. * @param string $type
  322. * @return \Doctrine\DBAL\Types\Type
  323. */
  324. protected function getDoctrineColumnType($type)
  325. {
  326. $type = strtolower($type);
  327. switch ($type) {
  328. case 'biginteger':
  329. $type = 'bigint';
  330. break;
  331. case 'smallinteger':
  332. $type = 'smallint';
  333. break;
  334. case 'mediumtext':
  335. case 'longtext':
  336. $type = 'text';
  337. break;
  338. case 'binary':
  339. $type = 'blob';
  340. break;
  341. }
  342. return Type::getType($type);
  343. }
  344. /**
  345. * Calculate the proper column length to force the Doctrine text type.
  346. *
  347. * @param string $type
  348. * @return int
  349. */
  350. protected function calculateDoctrineTextLength($type)
  351. {
  352. switch ($type) {
  353. case 'mediumText':
  354. return 65535 + 1;
  355. case 'longText':
  356. return 16777215 + 1;
  357. default:
  358. return 255 + 1;
  359. }
  360. }
  361. /**
  362. * Get the matching Doctrine option for a given Fluent attribute name.
  363. *
  364. * @param string $attribute
  365. * @return string|null
  366. */
  367. protected function mapFluentOptionToDoctrine($attribute)
  368. {
  369. switch ($attribute) {
  370. case 'type':
  371. case 'name':
  372. return;
  373. case 'nullable':
  374. return 'notnull';
  375. case 'total':
  376. return 'precision';
  377. case 'places':
  378. return 'scale';
  379. default:
  380. return $attribute;
  381. }
  382. }
  383. /**
  384. * Get the matching Doctrine value for a given Fluent attribute.
  385. *
  386. * @param string $option
  387. * @param mixed $value
  388. * @return mixed
  389. */
  390. protected function mapFluentValueToDoctrine($option, $value)
  391. {
  392. return $option == 'notnull' ? ! $value : $value;
  393. }
  394. }