Grammar.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <?php
  2. namespace Illuminate\Database\Schema\Grammars;
  3. use BackedEnum;
  4. use Illuminate\Contracts\Database\Query\Expression;
  5. use Illuminate\Database\Concerns\CompilesJsonPaths;
  6. use Illuminate\Database\Connection;
  7. use Illuminate\Database\Grammar as BaseGrammar;
  8. use Illuminate\Database\Schema\Blueprint;
  9. use Illuminate\Support\Fluent;
  10. use LogicException;
  11. use RuntimeException;
  12. abstract class Grammar extends BaseGrammar
  13. {
  14. use CompilesJsonPaths;
  15. /**
  16. * The possible column modifiers.
  17. *
  18. * @var string[]
  19. */
  20. protected $modifiers = [];
  21. /**
  22. * If this Grammar supports schema changes wrapped in a transaction.
  23. *
  24. * @var bool
  25. */
  26. protected $transactions = false;
  27. /**
  28. * The commands to be executed outside of create or alter command.
  29. *
  30. * @var array
  31. */
  32. protected $fluentCommands = [];
  33. /**
  34. * Compile a create database command.
  35. *
  36. * @param string $name
  37. * @param \Illuminate\Database\Connection $connection
  38. * @return void
  39. *
  40. * @throws \LogicException
  41. */
  42. public function compileCreateDatabase($name, $connection)
  43. {
  44. throw new LogicException('This database driver does not support creating databases.');
  45. }
  46. /**
  47. * Compile a drop database if exists command.
  48. *
  49. * @param string $name
  50. * @return void
  51. *
  52. * @throws \LogicException
  53. */
  54. public function compileDropDatabaseIfExists($name)
  55. {
  56. throw new LogicException('This database driver does not support dropping databases.');
  57. }
  58. /**
  59. * Compile a rename column command.
  60. *
  61. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  62. * @param \Illuminate\Support\Fluent $command
  63. * @param \Illuminate\Database\Connection $connection
  64. * @return array|string
  65. */
  66. public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
  67. {
  68. return sprintf('alter table %s rename column %s to %s',
  69. $this->wrapTable($blueprint),
  70. $this->wrap($command->from),
  71. $this->wrap($command->to)
  72. );
  73. }
  74. /**
  75. * Compile a change column command into a series of SQL statements.
  76. *
  77. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  78. * @param \Illuminate\Support\Fluent $command
  79. * @param \Illuminate\Database\Connection $connection
  80. * @return array|string
  81. *
  82. * @throws \RuntimeException
  83. */
  84. public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection)
  85. {
  86. throw new LogicException('This database driver does not support modifying columns.');
  87. }
  88. /**
  89. * Compile a fulltext index key command.
  90. *
  91. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  92. * @param \Illuminate\Support\Fluent $command
  93. * @return string
  94. *
  95. * @throws \RuntimeException
  96. */
  97. public function compileFulltext(Blueprint $blueprint, Fluent $command)
  98. {
  99. throw new RuntimeException('This database driver does not support fulltext index creation.');
  100. }
  101. /**
  102. * Compile a drop fulltext index command.
  103. *
  104. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  105. * @param \Illuminate\Support\Fluent $command
  106. * @return string
  107. *
  108. * @throws \RuntimeException
  109. */
  110. public function compileDropFullText(Blueprint $blueprint, Fluent $command)
  111. {
  112. throw new RuntimeException('This database driver does not support fulltext index removal.');
  113. }
  114. /**
  115. * Compile a foreign key command.
  116. *
  117. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  118. * @param \Illuminate\Support\Fluent $command
  119. * @return string
  120. */
  121. public function compileForeign(Blueprint $blueprint, Fluent $command)
  122. {
  123. // We need to prepare several of the elements of the foreign key definition
  124. // before we can create the SQL, such as wrapping the tables and convert
  125. // an array of columns to comma-delimited strings for the SQL queries.
  126. $sql = sprintf('alter table %s add constraint %s ',
  127. $this->wrapTable($blueprint),
  128. $this->wrap($command->index)
  129. );
  130. // Once we have the initial portion of the SQL statement we will add on the
  131. // key name, table name, and referenced columns. These will complete the
  132. // main portion of the SQL statement and this SQL will almost be done.
  133. $sql .= sprintf('foreign key (%s) references %s (%s)',
  134. $this->columnize($command->columns),
  135. $this->wrapTable($command->on),
  136. $this->columnize((array) $command->references)
  137. );
  138. // Once we have the basic foreign key creation statement constructed we can
  139. // build out the syntax for what should happen on an update or delete of
  140. // the affected columns, which will get something like "cascade", etc.
  141. if (! is_null($command->onDelete)) {
  142. $sql .= " on delete {$command->onDelete}";
  143. }
  144. if (! is_null($command->onUpdate)) {
  145. $sql .= " on update {$command->onUpdate}";
  146. }
  147. return $sql;
  148. }
  149. /**
  150. * Compile a drop foreign key command.
  151. *
  152. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  153. * @param \Illuminate\Support\Fluent $command
  154. * @return string
  155. */
  156. public function compileDropForeign(Blueprint $blueprint, Fluent $command)
  157. {
  158. throw new RuntimeException('This database driver does not support dropping foreign keys.');
  159. }
  160. /**
  161. * Compile the blueprint's added column definitions.
  162. *
  163. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  164. * @return array
  165. */
  166. protected function getColumns(Blueprint $blueprint)
  167. {
  168. $columns = [];
  169. foreach ($blueprint->getAddedColumns() as $column) {
  170. // Each of the column types has their own compiler functions, which are tasked
  171. // with turning the column definition into its SQL format for this platform
  172. // used by the connection. The column's modifiers are compiled and added.
  173. $sql = $this->wrap($column).' '.$this->getType($column);
  174. $columns[] = $this->addModifiers($sql, $blueprint, $column);
  175. }
  176. return $columns;
  177. }
  178. /**
  179. * Get the SQL for the column data type.
  180. *
  181. * @param \Illuminate\Support\Fluent $column
  182. * @return string
  183. */
  184. protected function getType(Fluent $column)
  185. {
  186. return $this->{'type'.ucfirst($column->type)}($column);
  187. }
  188. /**
  189. * Create the column definition for a generated, computed column type.
  190. *
  191. * @param \Illuminate\Support\Fluent $column
  192. * @return void
  193. *
  194. * @throws \RuntimeException
  195. */
  196. protected function typeComputed(Fluent $column)
  197. {
  198. throw new RuntimeException('This database driver does not support the computed type.');
  199. }
  200. /**
  201. * Add the column modifiers to the definition.
  202. *
  203. * @param string $sql
  204. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  205. * @param \Illuminate\Support\Fluent $column
  206. * @return string
  207. */
  208. protected function addModifiers($sql, Blueprint $blueprint, Fluent $column)
  209. {
  210. foreach ($this->modifiers as $modifier) {
  211. if (method_exists($this, $method = "modify{$modifier}")) {
  212. $sql .= $this->{$method}($blueprint, $column);
  213. }
  214. }
  215. return $sql;
  216. }
  217. /**
  218. * Get the command with a given name if it exists on the blueprint.
  219. *
  220. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  221. * @param string $name
  222. * @return \Illuminate\Support\Fluent|null
  223. */
  224. protected function getCommandByName(Blueprint $blueprint, $name)
  225. {
  226. $commands = $this->getCommandsByName($blueprint, $name);
  227. if (count($commands) > 0) {
  228. return reset($commands);
  229. }
  230. }
  231. /**
  232. * Get all of the commands with a given name.
  233. *
  234. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  235. * @param string $name
  236. * @return array
  237. */
  238. protected function getCommandsByName(Blueprint $blueprint, $name)
  239. {
  240. return array_filter($blueprint->getCommands(), function ($value) use ($name) {
  241. return $value->name == $name;
  242. });
  243. }
  244. /*
  245. * Determine if a command with a given name exists on the blueprint.
  246. *
  247. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  248. * @param string $name
  249. * @return bool
  250. */
  251. protected function hasCommand(Blueprint $blueprint, $name)
  252. {
  253. foreach ($blueprint->getCommands() as $command) {
  254. if ($command->name === $name) {
  255. return true;
  256. }
  257. }
  258. return false;
  259. }
  260. /**
  261. * Add a prefix to an array of values.
  262. *
  263. * @param string $prefix
  264. * @param array $values
  265. * @return array
  266. */
  267. public function prefixArray($prefix, array $values)
  268. {
  269. return array_map(function ($value) use ($prefix) {
  270. return $prefix.' '.$value;
  271. }, $values);
  272. }
  273. /**
  274. * Wrap a table in keyword identifiers.
  275. *
  276. * @param mixed $table
  277. * @return string
  278. */
  279. public function wrapTable($table)
  280. {
  281. return parent::wrapTable(
  282. $table instanceof Blueprint ? $table->getTable() : $table
  283. );
  284. }
  285. /**
  286. * Wrap a value in keyword identifiers.
  287. *
  288. * @param \Illuminate\Support\Fluent|\Illuminate\Contracts\Database\Query\Expression|string $value
  289. * @param bool $prefixAlias
  290. * @return string
  291. */
  292. public function wrap($value, $prefixAlias = false)
  293. {
  294. return parent::wrap(
  295. $value instanceof Fluent ? $value->name : $value, $prefixAlias
  296. );
  297. }
  298. /**
  299. * Format a value so that it can be used in "default" clauses.
  300. *
  301. * @param mixed $value
  302. * @return string
  303. */
  304. protected function getDefaultValue($value)
  305. {
  306. if ($value instanceof Expression) {
  307. return $this->getValue($value);
  308. }
  309. if ($value instanceof BackedEnum) {
  310. return "'{$value->value}'";
  311. }
  312. return is_bool($value)
  313. ? "'".(int) $value."'"
  314. : "'".(string) $value."'";
  315. }
  316. /**
  317. * Get the fluent commands for the grammar.
  318. *
  319. * @return array
  320. */
  321. public function getFluentCommands()
  322. {
  323. return $this->fluentCommands;
  324. }
  325. /**
  326. * Check if this Grammar supports schema changes wrapped in a transaction.
  327. *
  328. * @return bool
  329. */
  330. public function supportsSchemaTransactions()
  331. {
  332. return $this->transactions;
  333. }
  334. }