Builder.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. <?php
  2. namespace Illuminate\Database\Schema;
  3. use Closure;
  4. use Illuminate\Container\Container;
  5. use Illuminate\Database\Connection;
  6. use Illuminate\Support\Traits\Macroable;
  7. use InvalidArgumentException;
  8. use LogicException;
  9. class Builder
  10. {
  11. use Macroable;
  12. /**
  13. * The database connection instance.
  14. *
  15. * @var \Illuminate\Database\Connection
  16. */
  17. protected $connection;
  18. /**
  19. * The schema grammar instance.
  20. *
  21. * @var \Illuminate\Database\Schema\Grammars\Grammar
  22. */
  23. protected $grammar;
  24. /**
  25. * The Blueprint resolver callback.
  26. *
  27. * @var \Closure
  28. */
  29. protected $resolver;
  30. /**
  31. * The default string length for migrations.
  32. *
  33. * @var int|null
  34. */
  35. public static $defaultStringLength = 255;
  36. /**
  37. * The default relationship morph key type.
  38. *
  39. * @var string
  40. */
  41. public static $defaultMorphKeyType = 'int';
  42. /**
  43. * Create a new database Schema manager.
  44. *
  45. * @param \Illuminate\Database\Connection $connection
  46. * @return void
  47. */
  48. public function __construct(Connection $connection)
  49. {
  50. $this->connection = $connection;
  51. $this->grammar = $connection->getSchemaGrammar();
  52. }
  53. /**
  54. * Set the default string length for migrations.
  55. *
  56. * @param int $length
  57. * @return void
  58. */
  59. public static function defaultStringLength($length)
  60. {
  61. static::$defaultStringLength = $length;
  62. }
  63. /**
  64. * Set the default morph key type for migrations.
  65. *
  66. * @param string $type
  67. * @return void
  68. *
  69. * @throws \InvalidArgumentException
  70. */
  71. public static function defaultMorphKeyType(string $type)
  72. {
  73. if (! in_array($type, ['int', 'uuid', 'ulid'])) {
  74. throw new InvalidArgumentException("Morph key type must be 'int', 'uuid', or 'ulid'.");
  75. }
  76. static::$defaultMorphKeyType = $type;
  77. }
  78. /**
  79. * Set the default morph key type for migrations to UUIDs.
  80. *
  81. * @return void
  82. */
  83. public static function morphUsingUuids()
  84. {
  85. return static::defaultMorphKeyType('uuid');
  86. }
  87. /**
  88. * Set the default morph key type for migrations to ULIDs.
  89. *
  90. * @return void
  91. */
  92. public static function morphUsingUlids()
  93. {
  94. return static::defaultMorphKeyType('ulid');
  95. }
  96. /**
  97. * Create a database in the schema.
  98. *
  99. * @param string $name
  100. * @return bool
  101. *
  102. * @throws \LogicException
  103. */
  104. public function createDatabase($name)
  105. {
  106. throw new LogicException('This database driver does not support creating databases.');
  107. }
  108. /**
  109. * Drop a database from the schema if the database exists.
  110. *
  111. * @param string $name
  112. * @return bool
  113. *
  114. * @throws \LogicException
  115. */
  116. public function dropDatabaseIfExists($name)
  117. {
  118. throw new LogicException('This database driver does not support dropping databases.');
  119. }
  120. /**
  121. * Determine if the given table exists.
  122. *
  123. * @param string $table
  124. * @return bool
  125. */
  126. public function hasTable($table)
  127. {
  128. $table = $this->connection->getTablePrefix().$table;
  129. foreach ($this->getTables(false) as $value) {
  130. if (strtolower($table) === strtolower($value['name'])) {
  131. return true;
  132. }
  133. }
  134. return false;
  135. }
  136. /**
  137. * Determine if the given view exists.
  138. *
  139. * @param string $view
  140. * @return bool
  141. */
  142. public function hasView($view)
  143. {
  144. $view = $this->connection->getTablePrefix().$view;
  145. foreach ($this->getViews() as $value) {
  146. if (strtolower($view) === strtolower($value['name'])) {
  147. return true;
  148. }
  149. }
  150. return false;
  151. }
  152. /**
  153. * Get the tables that belong to the database.
  154. *
  155. * @return array
  156. */
  157. public function getTables()
  158. {
  159. return $this->connection->getPostProcessor()->processTables(
  160. $this->connection->selectFromWriteConnection($this->grammar->compileTables())
  161. );
  162. }
  163. /**
  164. * Get the names of the tables that belong to the database.
  165. *
  166. * @return array
  167. */
  168. public function getTableListing()
  169. {
  170. return array_column($this->getTables(), 'name');
  171. }
  172. /**
  173. * Get the views that belong to the database.
  174. *
  175. * @return array
  176. */
  177. public function getViews()
  178. {
  179. return $this->connection->getPostProcessor()->processViews(
  180. $this->connection->selectFromWriteConnection($this->grammar->compileViews())
  181. );
  182. }
  183. /**
  184. * Get the user-defined types that belong to the database.
  185. *
  186. * @return array
  187. */
  188. public function getTypes()
  189. {
  190. throw new LogicException('This database driver does not support user-defined types.');
  191. }
  192. /**
  193. * Determine if the given table has a given column.
  194. *
  195. * @param string $table
  196. * @param string $column
  197. * @return bool
  198. */
  199. public function hasColumn($table, $column)
  200. {
  201. return in_array(
  202. strtolower($column), array_map('strtolower', $this->getColumnListing($table))
  203. );
  204. }
  205. /**
  206. * Determine if the given table has given columns.
  207. *
  208. * @param string $table
  209. * @param array $columns
  210. * @return bool
  211. */
  212. public function hasColumns($table, array $columns)
  213. {
  214. $tableColumns = array_map('strtolower', $this->getColumnListing($table));
  215. foreach ($columns as $column) {
  216. if (! in_array(strtolower($column), $tableColumns)) {
  217. return false;
  218. }
  219. }
  220. return true;
  221. }
  222. /**
  223. * Execute a table builder callback if the given table has a given column.
  224. *
  225. * @param string $table
  226. * @param string $column
  227. * @param \Closure $callback
  228. * @return void
  229. */
  230. public function whenTableHasColumn(string $table, string $column, Closure $callback)
  231. {
  232. if ($this->hasColumn($table, $column)) {
  233. $this->table($table, fn (Blueprint $table) => $callback($table));
  234. }
  235. }
  236. /**
  237. * Execute a table builder callback if the given table doesn't have a given column.
  238. *
  239. * @param string $table
  240. * @param string $column
  241. * @param \Closure $callback
  242. * @return void
  243. */
  244. public function whenTableDoesntHaveColumn(string $table, string $column, Closure $callback)
  245. {
  246. if (! $this->hasColumn($table, $column)) {
  247. $this->table($table, fn (Blueprint $table) => $callback($table));
  248. }
  249. }
  250. /**
  251. * Get the data type for the given column name.
  252. *
  253. * @param string $table
  254. * @param string $column
  255. * @param bool $fullDefinition
  256. * @return string
  257. */
  258. public function getColumnType($table, $column, $fullDefinition = false)
  259. {
  260. $columns = $this->getColumns($table);
  261. foreach ($columns as $value) {
  262. if (strtolower($value['name']) === strtolower($column)) {
  263. return $fullDefinition ? $value['type'] : $value['type_name'];
  264. }
  265. }
  266. throw new InvalidArgumentException("There is no column with name '$column' on table '$table'.");
  267. }
  268. /**
  269. * Get the column listing for a given table.
  270. *
  271. * @param string $table
  272. * @return array
  273. */
  274. public function getColumnListing($table)
  275. {
  276. return array_column($this->getColumns($table), 'name');
  277. }
  278. /**
  279. * Get the columns for a given table.
  280. *
  281. * @param string $table
  282. * @return array
  283. */
  284. public function getColumns($table)
  285. {
  286. $table = $this->connection->getTablePrefix().$table;
  287. return $this->connection->getPostProcessor()->processColumns(
  288. $this->connection->selectFromWriteConnection($this->grammar->compileColumns($table))
  289. );
  290. }
  291. /**
  292. * Get the indexes for a given table.
  293. *
  294. * @param string $table
  295. * @return array
  296. */
  297. public function getIndexes($table)
  298. {
  299. $table = $this->connection->getTablePrefix().$table;
  300. return $this->connection->getPostProcessor()->processIndexes(
  301. $this->connection->selectFromWriteConnection($this->grammar->compileIndexes($table))
  302. );
  303. }
  304. /**
  305. * Get the names of the indexes for a given table.
  306. *
  307. * @param string $table
  308. * @return array
  309. */
  310. public function getIndexListing($table)
  311. {
  312. return array_column($this->getIndexes($table), 'name');
  313. }
  314. /**
  315. * Determine if the given table has a given index.
  316. *
  317. * @param string $table
  318. * @param string|array $index
  319. * @param string|null $type
  320. * @return bool
  321. */
  322. public function hasIndex($table, $index, $type = null)
  323. {
  324. $type = is_null($type) ? $type : strtolower($type);
  325. foreach ($this->getIndexes($table) as $value) {
  326. $typeMatches = is_null($type)
  327. || ($type === 'primary' && $value['primary'])
  328. || ($type === 'unique' && $value['unique'])
  329. || $type === $value['type'];
  330. if (($value['name'] === $index || $value['columns'] === $index) && $typeMatches) {
  331. return true;
  332. }
  333. }
  334. return false;
  335. }
  336. /**
  337. * Get the foreign keys for a given table.
  338. *
  339. * @param string $table
  340. * @return array
  341. */
  342. public function getForeignKeys($table)
  343. {
  344. $table = $this->connection->getTablePrefix().$table;
  345. return $this->connection->getPostProcessor()->processForeignKeys(
  346. $this->connection->selectFromWriteConnection($this->grammar->compileForeignKeys($table))
  347. );
  348. }
  349. /**
  350. * Modify a table on the schema.
  351. *
  352. * @param string $table
  353. * @param \Closure $callback
  354. * @return void
  355. */
  356. public function table($table, Closure $callback)
  357. {
  358. $this->build($this->createBlueprint($table, $callback));
  359. }
  360. /**
  361. * Create a new table on the schema.
  362. *
  363. * @param string $table
  364. * @param \Closure $callback
  365. * @return void
  366. */
  367. public function create($table, Closure $callback)
  368. {
  369. $this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
  370. $blueprint->create();
  371. $callback($blueprint);
  372. }));
  373. }
  374. /**
  375. * Drop a table from the schema.
  376. *
  377. * @param string $table
  378. * @return void
  379. */
  380. public function drop($table)
  381. {
  382. $this->build(tap($this->createBlueprint($table), function ($blueprint) {
  383. $blueprint->drop();
  384. }));
  385. }
  386. /**
  387. * Drop a table from the schema if it exists.
  388. *
  389. * @param string $table
  390. * @return void
  391. */
  392. public function dropIfExists($table)
  393. {
  394. $this->build(tap($this->createBlueprint($table), function ($blueprint) {
  395. $blueprint->dropIfExists();
  396. }));
  397. }
  398. /**
  399. * Drop columns from a table schema.
  400. *
  401. * @param string $table
  402. * @param string|array $columns
  403. * @return void
  404. */
  405. public function dropColumns($table, $columns)
  406. {
  407. $this->table($table, function (Blueprint $blueprint) use ($columns) {
  408. $blueprint->dropColumn($columns);
  409. });
  410. }
  411. /**
  412. * Drop all tables from the database.
  413. *
  414. * @return void
  415. *
  416. * @throws \LogicException
  417. */
  418. public function dropAllTables()
  419. {
  420. throw new LogicException('This database driver does not support dropping all tables.');
  421. }
  422. /**
  423. * Drop all views from the database.
  424. *
  425. * @return void
  426. *
  427. * @throws \LogicException
  428. */
  429. public function dropAllViews()
  430. {
  431. throw new LogicException('This database driver does not support dropping all views.');
  432. }
  433. /**
  434. * Drop all types from the database.
  435. *
  436. * @return void
  437. *
  438. * @throws \LogicException
  439. */
  440. public function dropAllTypes()
  441. {
  442. throw new LogicException('This database driver does not support dropping all types.');
  443. }
  444. /**
  445. * Rename a table on the schema.
  446. *
  447. * @param string $from
  448. * @param string $to
  449. * @return void
  450. */
  451. public function rename($from, $to)
  452. {
  453. $this->build(tap($this->createBlueprint($from), function ($blueprint) use ($to) {
  454. $blueprint->rename($to);
  455. }));
  456. }
  457. /**
  458. * Enable foreign key constraints.
  459. *
  460. * @return bool
  461. */
  462. public function enableForeignKeyConstraints()
  463. {
  464. return $this->connection->statement(
  465. $this->grammar->compileEnableForeignKeyConstraints()
  466. );
  467. }
  468. /**
  469. * Disable foreign key constraints.
  470. *
  471. * @return bool
  472. */
  473. public function disableForeignKeyConstraints()
  474. {
  475. return $this->connection->statement(
  476. $this->grammar->compileDisableForeignKeyConstraints()
  477. );
  478. }
  479. /**
  480. * Disable foreign key constraints during the execution of a callback.
  481. *
  482. * @param \Closure $callback
  483. * @return mixed
  484. */
  485. public function withoutForeignKeyConstraints(Closure $callback)
  486. {
  487. $this->disableForeignKeyConstraints();
  488. try {
  489. return $callback();
  490. } finally {
  491. $this->enableForeignKeyConstraints();
  492. }
  493. }
  494. /**
  495. * Execute the blueprint to build / modify the table.
  496. *
  497. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  498. * @return void
  499. */
  500. protected function build(Blueprint $blueprint)
  501. {
  502. $blueprint->build($this->connection, $this->grammar);
  503. }
  504. /**
  505. * Create a new command set with a Closure.
  506. *
  507. * @param string $table
  508. * @param \Closure|null $callback
  509. * @return \Illuminate\Database\Schema\Blueprint
  510. */
  511. protected function createBlueprint($table, ?Closure $callback = null)
  512. {
  513. $prefix = $this->connection->getConfig('prefix_indexes')
  514. ? $this->connection->getConfig('prefix')
  515. : '';
  516. if (isset($this->resolver)) {
  517. return call_user_func($this->resolver, $table, $callback, $prefix);
  518. }
  519. return Container::getInstance()->make(Blueprint::class, compact('table', 'callback', 'prefix'));
  520. }
  521. /**
  522. * Get the database connection instance.
  523. *
  524. * @return \Illuminate\Database\Connection
  525. */
  526. public function getConnection()
  527. {
  528. return $this->connection;
  529. }
  530. /**
  531. * Set the database connection instance.
  532. *
  533. * @param \Illuminate\Database\Connection $connection
  534. * @return $this
  535. */
  536. public function setConnection(Connection $connection)
  537. {
  538. $this->connection = $connection;
  539. return $this;
  540. }
  541. /**
  542. * Set the Schema Blueprint resolver callback.
  543. *
  544. * @param \Closure $resolver
  545. * @return void
  546. */
  547. public function blueprintResolver(Closure $resolver)
  548. {
  549. $this->resolver = $resolver;
  550. }
  551. }